繁体   English   中英

确定浮点数是否是另一个浮点数的倍数?

[英]Determine if a floating point number is a multiple of another floating point number?

我需要使用JavaScript确定浮点数是否为另一个浮点数的倍数。

我已经阅读了一些其他有关浮点数的问题,并且了解到它们不能与模运算符(%)一起正常工作。 我还读过您可以通过乘以10/100/1000等将浮点数转换为整数,但是这在所有情况下均无法正常工作。

例:

var A = 25.13;
var B = .0001;

var Value = A*1e5;
var Step  = B*1e5;

// Is Value a multiple of Step?
if(0 === (Value % Step)) {
// Do something
}

在这种情况下,Value是Step的倍数,并且可以正常工作。 但是关于:

var A = 2.2;
var B = .0001;

它应该是有效的倍数,但是我们得到:

220000.00000000003 % 10 = 2.9103830456733704e-11

小数点后11位错误3。 我认为我可以通过执行以下操作来纠正与toFixed()舍入的问题:

var Value = (A*1e5).toFixed(10);
var Step  = (B*1e5).toFixed(10);

但是,如果您这样做:

var A = 45436212356482;
var B = .0001;

你得到:

4543621235648200192.0000000000 % 10.0000000000=2

这是一个有效的倍数,但它认为不是。

附:

var A = 45436212546522156.45621565421;
var B = .0001;

这不是有效的倍数,但它认为是:

4.543621254652216e+21 % 10.0000000000=0

确定一个浮点数是否是另一个浮点数的倍数有聪明的技巧吗? 还是这不可能?

更新:

目的是将用户输入的数字(整数或十进制)限制为一定的增量。

  • 如果增量为1,则用户可以输入1、2、3、4等。
  • 如果“增量”为.5,则用户可以输入.5,1,1.5,2,2.5,依此类推。
  • 如果增量为.0002,则用户可以输入1,1.001、1.0004、1.0006,但不能输入1.0001

从逻辑角度来看,给定值是给定增量的有效倍数或不是该值的有效倍数。

给出您的最后两个示例(具有4543621235648200192等),看来您想接受.0001的整数倍,并拒绝不是的数字, 并且您不想使用.0001而是使用包含浮点数的变量来做到这一点点值最接近.0001。

通常,这是不可能的。 当您传递其他内容时,算法无法“知道” .0001的意图。

如果您进一步限制问题,则可能有解决方案。 例如,可以(也许不容易)回答这个问题:浮点值X浮点值是否最接近.0001的整数倍? (换句话说,是否存在一个整数k,以至于将.0001与k相乘并四舍五入到最接近的浮点值会产生X?)

因此,要获得解决方案,您需要进一步描述您的目标。 您是否需要针对Step的任意值或仅某些值的解决方案? 由于二进制浮点不能足够精确地表示Step,因此您还有其他方法来描述它吗? 例如,它将始终是.0001的倍数吗? 您要接受的值是否始终是最接近精确数学倍数的二进制浮点数,还是可能会有其他错误?

基本问题是32位浮点数不能正确表示所有实数。 例如,如果用户输入0.1,则浮点值将为0.099999994。

因此,如果增量为0.1,则无法确定他是否输入了0.1(这是有效的),还是输入了0.09999 ..(这是无效的)。

我的建议是改用整数数据类型,并将其视为定点数。 这样,您就不会失去精度,并且可以轻松地检查多重性。

如果我错了,请纠正我,但是可以使用以下解决方案:

 function IsMultipleOf(a, b) { var result = a % b; return (result < 1e-3); } 

只能回答一半的问题。 假设以下内容(使用python代码):

dt = 2.4
>>> dt = 2.2999999999999998
t = 13 * dt
>>> t = 29.899999999999999
t % dt
>>> 8.8817841970012523e-16

在这种情况下,它将正常工作。 现在假设:

dt = 1.4
>>> dt = 1.3999999999999999
t = 3 * dt
>>> t = 4.1999999999999993
t % dt
>>> 1.3999999999999995

由于舍入误差的作用是使t小于dt的下一个倍数,所以模的值更接近dt而不是0。

解决此问题的一种可能方法是检查两种情况:

modt = t % dt
(abs(modt) <= tolerance) or (abs(dt - modt) <= tolerance)

在这个答案的基础上 ,我希望tolerance = machine epsilon * abs(dt) / 2 ,但是取模运算会以某种方式引入更多的误差。 看起来像:

tolerance = machine epsilon * max(abs(t), abs(dt)) / 2

处理这份工作,但这只是一个猜测。 或者,

tolerance = 2 * machine epsilon * abs(dt)

似乎还可以。

以下将适用于所有小数。

var result = Math.round( Math.round(number1 * 100000) % Math.round(number2 * 100000) ) / 100000;

这是您可以执行的一种方法:

function floatingPointAMultipleOfB(a, b) {
  const precision_a = getNumbersAfterDecimal(a)
  const precision_b = getNumbersAfterDecimal(b)

  if (precision_a > precision_b) return false;

  const int_a = Math.round(multBy10(a, precision_b))
  const int_b = Math.round(multBy10(b, precision_b))
  return int_a % int_b === 0
}


function getNumbersAfterDecimal(n) {
  const exponential = n.toString().split('e');
  if (exponential.length === 2) n = n.toFixed(Math.abs(exponential[1]))
  return (n.toString().split('.')[1] || []).length;
}

function multBy10(val, n) {
  if (n === 0) return val
  return multBy10(val, n-1) * 10
}

由于您正在处理浮点数,因此最好的选择是确定“关闭”关闭的程度,并使用它:

function IsMultipleOf(a, b) {
   var result = a % b;
   return (result < 1e-3);
}

我需要使用JavaScript确定浮点数是否为另一个浮点数的倍数。

如果我们假设使用浮点数近似于实数,那么根据定义,每个浮点数都是另一个数的倍数。 当然,由于浮点数实际上是有理数的子集,因此当然会有很多对的浮点数没有共同的除数。 例如,尾数中具有不同素数系数的任何两个浮点数没有公共除数。 指数的范围进一步将对限制为精确的倍数。

但是,也许您不是在寻找具有任意乘数的数字,而是在寻找具有整数除数的数字,然后测试结果是否低于所选阈值(通常称为epsilon)。

例如功能风格的伪代码!

fmod :: float -> float -> float
fmod a b =
    b - a * floor( b / a )

EPSILON = 1e-5;
divides_to_integer :: float -> float -> boolean
divides_to_integer a b =
    fmod(a, b) < EPSILON

fmod函数应来自JavaScript数学库。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM