精度损失 – int – > float或double
我有一个我正在修改的考试题目,问题是4分。
“在java中,我们可以将int赋给double或float”。 这会丢失信息吗?为什么?
我已经说过,因为int通常具有固定的长度或大小 – 存储数据的精度是有限的,其中以浮点存储信息可能是无限的,基本上我们丢失信息因为这个
现在我有点粗略地说我是否在这里击中了正确的区域。 我确信它会失去精确度,但我无法完全理解为什么。 我能得到一些帮助吗?
在Java中,Integer使用32位来表示其值。
在Java中,FLOAT使用23位尾数,因此大于2 ^ 23的整数将截断其最低有效位。 例如,33554435(或0x200003)将被截断为大约33554432 +/- 4
在Java中,DOUBLE使用52位尾数,因此能够表示32位整数而不会丢失数据。
另请参阅维基百科上的“ 浮点 ”
没有必要知道浮点数的内部布局。 您所需要的只是鸽子原理以及int
和float
大小相同的知识。
-
int
是32位类型,每个位模式表示一个不同的整数,因此有2 ^ 32个int
值。 -
float
是32位类型,因此它最多有2 ^ 32个不同的值。 - 一些
float
表示非整数,因此表示整数的float
值少于 2 ^ 32。 - 因此,不同的
int
值将转换为相同的float
(=精度损失)。
类似的推理可以用于long
和double
。
以下是JLS对此事的评论(在非技术性讨论中)。
JLS 5.1.2拓宽原始转换
以下19种关于基本类型的特定转换称为扩展基元转换:
int
到long
,float
或double
- (其余省略)
将
int
或long
值转换为float
,或将long
值转换为double
,可能会导致精度损失 – 也就是说,结果可能会丢失该值的一些最低有效位。 在这种情况下,使用IEEE 754舍入到最接近模式,得到的浮点值将是整数值的正确舍入版本。尽管可能发生精度损失,但原始类型之间的扩展转换不会导致运行时exception。
这是一个失去精度的扩展转换的示例:
class Test { public static void main(String[] args) { int big = 1234567890; float approx = big; System.out.println(big - (int)approx); } }
打印:
-46
因此表明在从类型
int
转换为float
类型的过程中信息丢失,因为float
类型的值不精确到九位有效数字。
不, float
和double
也是固定长度的 – 它们只是使用不同的位。 阅读有关它们在Floating-Poing指南中的确切工作原理的更多信息。
基本上,在将double
int
double
时不会失去精度,因为double
有52位精度,足以容纳所有int
值。 但是float
只有23位的精度,因此它不能精确地表示大于约2 ^ 23的所有int
值。
可能是我见过的最明确的解释: http : //www.ibm.com/developerworks/java/library/j-math2/index.html ULP或精度最低的单位定义了任何两个浮点值之间的可用精度。 随着这些值的增加,可用精度降低。 例如:介于1.0和2.0之间,有8,388,609个浮点数,1,000,000和1,000,001之间有17个。在10,000,000个ULP是1.0,所以高于这个值你很快会有多个整数值映射到每个可用的浮点数,因此精度会降低。
你的直觉是正确的,你可以在将int
转换为float
时松开精度。 然而,它并不像大多数其他答案中那样简单。
在Java中,FLOAT使用23位尾数,因此大于2 ^ 23的整数将截断其最低有效位。 (来自本页的post)
不对。
示例:这是一个大于2 ^ 23的整数,它转换为一个没有丢失的浮点数:
int i = 33_554_430 * 64; // is greater than 2^23 (and also greater than 2^24); i = 2_147_483_520 float f = i; System.out.println("result: " + (i - (int) f)); // Prints: result: 0 System.out.println("with i:" + i + ", f:" + f);//Prints: with i:2_147_483_520, f:2.14748352E9
因此,大于2 ^ 23的整数将截断其最低有效位并不是真的。
我找到的最佳解释是:
Java中的浮点数为32位,表示为:
sign *尾数* 2 ^指数
标志*(0至33_554_431)* 2 ^( – 125至+127)
资料来源: http : //www.ibm.com/developerworks/java/library/j-math2/index.html
为什么这是一个问题?
它留下的印象是, 只要查看 int的大小,就可以确定是否存在从int到float的精度损失。
我特别看到了Java考试问题,其中一个被问到一个大的int是否会转换为一个没有丢失的浮点数。
此外,有时人们倾向于认为从int到float会有精度损失:
当int大于:1_234_567_890 不为真时 (参见上面的反例)
当int大于:2指数23(等于:8_388_608) 不为真
当int大于:2指数24(等于:16_777_216)时不为真
结论
从足够大的整数到浮点数的转换可能会失去精度。
仅仅通过查看 int的大小来确定是否会有损失(即不试图深入到实际的浮点表示中)是不可能的。
将int赋值给double或float可能会失去精度有两个原因:
- 有些数字不能表示为double / float,因此它们最终会近似
- 大整数可能在租约有效数字中包含太多精度
对于这些示例,我使用的是Java。
使用这样的函数来检查从int转换为float时的精度损失
static boolean checkPrecisionLossToFloat(int val) { if(val < 0) { val = -val; } // 8 is the bit-width of the exponent for single-precision return Integer.numberOfLeadingZeros(val) + Integer.numberOfTrailingZeros(val) < 8; }
使用这样的函数来检查从长到两次铸造时的精度损失
static boolean checkPrecisionLossToDouble(long val) { if(val < 0) { val = -val; } // 11 is the bit-width for the exponent in double-precision return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 11; }
使用这样的函数来检查从long转换为float时的精度损失
static boolean checkPrecisionLossToFloat(long val) { if(val < 0) { val = -val; } // 8 + 32 return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 40; }
对于这些函数中的每一个,返回true意味着将该整数值强制转换为浮点值将导致精度损失。
如果积分值超过24个有效位,则转换为float将失去精度。
如果积分值超过53个有效位,则转换为double将失去精度。