簡體   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