当前位置: 首页 > 知识库问答 >
问题:

从float到int的精确转换

钮承恩
2023-03-14

我想将浮点值转换为int值或抛出异常,如果这种转换是不准确的。

我找到了以下建议:使用Math.round来转换,然后使用==来检查这些值是否相等。如果它们相等,那么转换是精确的,否则就不是。

但是我发现了一个不起作用的例子。下面是演示这个例子的代码:

        String s = "2147483648";

        float f = Float.parseFloat(s);
        System.out.printf("f=%f\n", f);

        int i = Math.round(f);
        System.out.printf("i=%d\n", i);

        System.out.printf("(f == i)=%s\n", (f == i));

它输出:

f=2147483648.000000
i=2147483647
(f == i)=true

我知道2147483648不适合整数范围,但我很惊讶=为这些值返回true。有没有更好的方法来比较float和int?我想可以将这两个值都转换为字符串,但对于这样一个基本函数来说,这将非常缓慢。

共有1个答案

田冥夜
2023-03-14

浮动是相当不精确的概念。它们也基本上毫无意义,除非你现在运行的是相当旧的硬件,或者专门与浮点数工作的系统和/或协议进行交互,或者在它们的规范中有“使用浮点数”硬编码。这可能是真的,但如果不是,停止使用float,开始使用double——除非你有一个相当大的float[]内存和性能差为零,否则float就不那么准确了。

当使用intvsdouble时,您的算法不会失败-所有int都可以完美地表示为double

这里潜在的错误是“无声铸造”的概念,以及java如何在那里采取了一些有意的自由。

在一般的计算机系统中,你只能比较同类。很容易用精确的位和机器码来表示,如果a和b是完全相同的类型,那么判断a==b是真是假意味着什么。当a和b是不同的东西时,这一点都不清楚。这一点几乎适用于任何运营商AB,如果两者都是int,则这是一种清晰易懂的操作。但是如果acharb是,比如说,adouble,那就完全不清楚了。

因此,在java中,所有涉及不同类型的二进制运算符都是非法的。例如,在BASIC中,没有字节码可以直接比较浮点和双精度,或者将字符串添加到int。

然而,还有语法糖:当你写a==b时,其中a和b是不同的类型,而java确定其中一种类型是另一种类型的“子集”,那么java只需默默地将“较小”类型转换为“较大”类型,这样操作就可以成功。例如:

int x = 5;
long y = 5;
System.out.println(x == y);

这是可行的——因为java意识到将int值转换为long值是不会失败的,所以明确指定您希望代码这样做并不麻烦。在JLS术语中,这被称为扩大转换。相比之下,任何将“较大”类型转换为“较小”类型的尝试都是不合法的,您必须显式强制转换:

long x = 5;
int y = x; // does not compile
int y = (int) x; // but this does.

要点很简单:当你写上述相反的代码(intx=5;long y=x;)时,代码是相同的,只是编译器在不会发生丢失的基础上,默默地为你注入了(long)。同样的事情也发生在这里:

int x = 5;
long y = 10;
long z = x + y;

之所以编译,是因为javac为您添加了一些语法糖,具体来说,编译时就像它说的那样:long z=((long)x)y 。表达式xy的“类型”是long

这里是关键技巧:Java考虑将int转换为float,以及将int或long转换为double-a。

与中一样,javac只会假设它可以安全地完成这项工作,而不会造成任何损失,因此不会强制程序员通过手动添加强制转换来明确确认。但是,int-

浮点数可以表示-2^23到2^23之间的每个整数值,双精度浮点数可以表示-2^52到2^52之间的每个整数值(源)。但是int可以表示-2^312^31-1,以及长-2^632^63-1之间的每个整数值。这意味着在边缘(非常大的负数/正数),存在整数,它们可以用整数表示,但不能用浮点数表示,或者用长整数表示,但不能用双精度表示(幸运的是,所有整数都可以用双精度表示;int-

这里就是这样:(f==i)被语法糖化为(f==((float)i)),从int到float的转换引入了舍入。

大多数情况下,当你使用双精度和浮点数,却希望得到准确的数字时,你已经搞砸了。从根本上说,这些概念并不精确,这种精确性不能通过试图解释误差带而被附带进来,因为由于float和double的舍入行为而引入的误差无法被跟踪(无论如何都不容易)。因此,不应使用float/double。找到一个原子单位,用int/long表示,或者使用BigDecimal。(例如:编写记账软件时,不要将财务金额存储为双精度。请将其存储长格式的“美分”(或satoshis、日元、便士或该货币中的任何原子单位),或者,如果您真的知道自己在做什么,请使用大十进制)。

如果你绝对肯定在这里使用float(甚至double)是可以接受的,并且你仍然想要精确性,我们有一些解决方案。

选项1是使用BigDecimal的幂:

new BigDecimal(someDouble).intValueExact()

这是可行的,是100%可靠的(除非浮点到双精度的转换可以以某种方式将非精确值转换为精确值,我认为这不可能发生),并且抛出。速度也很慢。

另一种方法是利用我们对IEEE浮点标准工作原理的了解。

一个真正简单的答案是在编写算法时运行它,但是要添加一个额外的检查:如果你的int得到的值低于-2^23或高于2^23,那么它可能是不正确的。然而,在-2^23和2^23下面仍然有一些数字,它们在浮点数和int中都可以完美地表示,只是,不再是那个时候的每一个数字。如果你想要一个算法也能接受这些精确的数字,那么事情就变得复杂多了。我的建议是不要深究这个污水池:如果你有一个过程,你最终得到了一个接近这种极端的浮点数,并且你想把它们变成int,但只有在没有损失,你已经到达了一个疯狂的问题,你需要重新连接你得到的部分!

如果您真的需要它,我建议您使用BigDecimal(),而不是尝试使用float。intValueExact()如果你真的必须拥有这个技巧。

 类似资料:
  • 我试图在Textpad中复制这个Java程序,但我收到以下错误 C:\Users\User\Desktop\java\Drawing.java:14: 错误: 不兼容的类型: 从 float 到 int g.drawLine ((getWidth()/2) , 0, (getWidth()*i) , (getHeight()/2)); 这是代码 在getWidth*i之前我已经尝试过添加(floa

  • 问题内容: 我有以下代码,我想为浮点数指定一个十进制值而又不损失精度。 输出: 5.88 预期输出: 525.880005 问题答案: 仅具有7-8位有效数字。您的示例中的“ 5”是第9位数字。 即使它具有足够的精度,我也不知道525.880005是否可以精确表示为二进制浮点数。大多数十进制值不是:) 如果确切的十进制表示形式对您很重要,则应使用。

  • 我一直在尝试编译,并玩了一圈的和数,但仍然不能弄清楚错误是什么。有什么想法吗? 标题:

  • 之前,我已经实施了此功能,并且它起到了作用: 我甚至可以用下面的东西检查我是否得到了正确的尺寸,一切都很好。 但是,在使用以下初始化尝试整个程序时,我得到了一个编译错误: 以下是错误消息: rbm. cpp:在函数“ululemexFunction(int, mxArray**, int, const mxArray**)”中:rbm. cpp: 570:64: error:从“int”到“int

  • 我想在Swift中将转换为。像这样的基本强制转换不起作用,因为这些类型不是原语,不像Objective-C中的s和s

  • 问题内容: 我想将转换成斯威夫特。像这样的基本转换不起作用,因为这些类型不是基元,这与Objective-C中的s和s 不同 但这会产生以下错误消息: “浮点数”不可转换为“整数” 知道如何将属性从转换为吗? 问题答案: 您可以转换到斯威夫特这样的: 您还可以使用@paulm的注释来实现此结果: