[英]Error on casting unsigned int to float
對於以下程序。
#include <stdio.h>
int main()
{
unsigned int a = 10;
unsigned int b = 20;
unsigned int c = 30;
float d = -((a*b)*(c/3));
printf("d = %f\n", d);
return 0;
}
輸出是
d = 4294965248.000000
當我更改表達式中的幻數3
以將d計算為3.0
,我得到了正確的結果:
d = 2000.000000
如果我將a,b,c的類型更改為int
,那么我也會得到正確的結果。
我猜這個錯誤是由於從unsigned int
到float
的轉換而發生的,但是我不知道有關如何創建奇怪結果的詳細信息。
我認為您意識到在將float
賦值之前,將負號轉換為unsigned int
。 如果運行以下代碼,則很有可能會產生4294965296
#include <stdio.h>
int main()
{
unsigned int a = 10;
unsigned int b = 20;
unsigned int c = 30;
printf("%u", -((a*b)*(c/3)));
return 0;
}
等號右邊的
-2000
設置為有符號整數(大小0xFFFFF830
32位),十六進制值為0xFFFFF830
。 編譯器生成代碼以將此有符號整數移動到也是32位實體的無符號整數x
中。 編譯器假定您在等號右邊僅具有一個正值,因此它將所有32位簡單地移動到x
。x
現在具有值0xFFFFF830
,如果將其解釋為正數,則該值為4294965296
。 但是%d
的printf
格式表示32位將被解釋為有符號整數,因此您得到-2000
。 如果使用%u
,它將被打印為4294965296
。
#include <stdio.h>
#include <limits.h>
int main()
{
float d = 4294965296;
printf("d = %f\n\n", d);
return 0;
}
當您將4294965296
轉換為float
,使用的數字很長,無法放入小數部分。 現在已經失去了一些精度。 由於損失,我得到了4294965248.000000
。
IEEE-754浮點標准是用於表示和處理所有現代計算機系統所遵循的浮點數量的標准。
bit 31 30 23 22 0 S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
從最低有效位開始計數。 第一位是符號(
0
表示正,1
表示負)。 以下8
位是Extra-127二進制符號的指數; 這意味着該二進制模式01111111 = 127
表示的指數0
,1000000 = 128
,表示1,01111110 = 126
代表-1
,等等。 尾數適合其余的24
位,如上所述,其前導1
被剝離。 資源
如您所見,將4294965296
轉換為float
,會發生精度為00011000
損失。
11111111111111111111100 00011000 0 <-- 4294965296
11111111111111111111100 00000000 0 <-- 4294965248
您的整個計算將無符號完成,因此與
float d = -(2000u);
-2000
in unsigned int
(假設4294965295
int
)是4294965295
這將被寫入您的float d
。 但是由於float
無法保存此確切數字,因此將其另存為4294965248
。
根據經驗,浮點數的精度為7個有效基數10位。
計算得出的結果是2^32 - 2000
,然后其余部分由浮點精度完成。
如果您改為使用3.0
則會按以下方式更改計算中的類型
float d = -((a*b)*(c/3.0));
float d = -((unsigned*unsigned)*(unsigned/double));
float d = -((unsigned)*(double));
float d = -(double);
為您提供正確的負值。
這是因為您對unsigned int
使用-
。 -
反轉數字的位。 讓我們打印一些無符號整數:
printf("Positive: %u\n", 2000);
printf("Negative: %u\n", -2000);
// Output:
// Positive: 2000
// Negative: 4294965296
讓我們打印十六進制值:
printf("Positive: %x\n", 2000);
printf("Negative: %x\n", -2000);
// Output
// Positive: 7d0
// Negative: fffff830
如您所見,位是反轉的。 因此,問題出在unsigned int
上使用-
,而不是將unsigned int
強制轉換為float。
正如其他人所說,問題是您正試圖取消一個未簽名的數字。 已經給出的大多數解決方案使您進行某種形式的浮點轉換,以便對浮點類型進行算術運算。 另一種解決方案是將算術結果轉換為int
然后取反,這樣算術運算將在整數類型上進行,根據您的實際用例,該類型可能會或可能不會更好:
#include <stdio.h>
int main(void)
{
unsigned int a = 10;
unsigned int b = 20;
unsigned int c = 30;
float d = -(int)((a*b)*(c/3));
printf("d = %f\n", d);
return 0;
}
-((a*b)*(c/3));
全部以無符號整數算術 ( 包括一元否定)執行。 一元否定對於無符號類型定義明確:在數學上,結果為2 N模,其中N是unsigned int
的位數。 當您將較大的數字分配給float
,會遇到一些精度損失。 由於其二進制大小,結果是與將2048相除的unsigned int
最接近的數字。
如果將3更改為3.0,則c / 3.0
是double
類型,因此a * b
的結果在轉換之前將轉換為double
精度。 然后將此double
精度數分配給float
,並且已經觀察到精度損失。
您需要將整數轉換為浮點數
float d = -((a*b)*(c/3));
至
float d = -(((float)a*(float)b)*((float)c/3.0));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.