java - 如何将 double 转换为 Java 中的精确小数?

标签 java function math methods

用技术术语来说,我需要一种方法来转换 IEEE 754 binary64 number转换为两个 BigInteger 的缩小比率,这两个 BigInteger 在数学上表示完全相同的值。该方法不需要处理 infinite 的值或 NaN , 但它确实需要处理 subnormalssigned zeros .由于 IEEE 754 binary64 数字格式不支持表示无理数,因此该任务在理论上是可行的。

以下是一些示例值:

  • 0.0 = 0/1
  • -0.0 = 0/1
  • 0.5 = 1/2
  • 0.1 = 3602879701896397/36028797018963968
  • 1/(双)3 = 6004799503160661/18014398509481984
  • Double.MIN_NORMAL = 1/2^1022 = 1/44942328371557897693232629769725618340449424473557664318357520289433168951375240783177119330601884005280028469967848339414697442203604155623211857659868531094441973356216371319075554900311523529863270738021251442209537670585615720368478277635206809290837627671146574559986811484619929076208839082406056034304
  • Double.MIN_VALUE = 1/2^1074 = 1/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784
  • Double.MAX_VALUE = (2^1024 - 2^971)/1 = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368/1

最佳答案

此方法检查 double 位以防止舍入错误。

在 double 中,第一位是符号,接下来的 11 位是指数,最后的 52 位是尾数。

我发现将整个值与 0 进行比较更容易,而不是检查第一位。

我没有获取指数的位和处理指数的符号(不同于值的符号),而是使用 Math.getExponent 来获取它的有符号值。根据其documentation ,

  • If the argument is NaN or infinite, then the result is Double.MAX_EXPONENT + 1.
  • If the argument is zero or subnormal, then the result is Double.MIN_EXPONENT -1.

如果该值不低于正规值,则有效数在其 52 位之前有一个隐含的前导 1。指数假定二进制小数点(即小数点)在前导 1 之后,因此我从指数中减去 52 以将二进制小数点移到末尾。

public static BigInteger[] convertToFraction(double value) {
  int exponent = Math.getExponent(value);
  if (exponent > Double.MAX_EXPONENT) {
    // The value is infinite or NaN.
    throw new IllegalArgumentException("Illegal parameter 'value': " + value);
  }
  long positiveSignificand;
  if (exponent < Double.MIN_EXPONENT) {
    // The value is subnormal.
    exponent++;
    positiveSignificand = Double.doubleToLongBits(value) & 0x000fffffffffffffL;
  } else {
    positiveSignificand = (Double.doubleToLongBits(value) & 0x000fffffffffffffL) | 0x0010000000000000L;
  }
  BigInteger significand = BigInteger.valueOf(value < 0 ? -positiveSignificand : positiveSignificand);
  exponent -= 52; // Adjust the exponent for an integral significand.
  BigInteger coefficient = BigInteger.ONE.shiftLeft(Math.abs(exponent));
  if (exponent >= 0) {
    return new BigInteger[] { significand.multiply(coefficient), BigInteger.ONE };
  } else {
    BigInteger gcd = significand.gcd(coefficient);
    return new BigInteger[] { significand.divide(gcd), coefficient.divide(gcd) };
  }
}

关于java - 如何将 double 转换为 Java 中的精确小数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27259162/

相关文章:

python - 如何添加不等长列表中的每个元素?

math - 计算地球平面 map 上两点之间的最短路径

java - 从 java HttpClient 使用 kerberos 验证共享点

java - java是否缓存方法的结果

javascript - 为什么我不能从 JavaScript 中的另一个函数调用一个函数?

vb.net - 从 Visual Basic 中的结构中的函数返回值

Java TreeMap 获取第 K 个最小的键

java - 在 Spring 中使用用户输入的用户名和密码发送邮件

java - 如何求和之字形算法

c - 为什么 Ackermann 函数在这个用 C 语言编写的 Mouse 解释器中需要这么长时间?