Extracting a specific digit from a float w/o floating-point implementation rounding issues
29 次查看(过去 30 天)
显示 更早的评论
I have a set of base-10 nonnegative floats with either three or four digits to the right of the decimal point. Can anyone suggest a reliable way to extract the very rightmost digit (that is, the 1E-4 place) without running into occasional floating-point issues?
My first attempt, lastDigit = round(10*rem(num*1000,1)), works most of the time:
>> num = 130404809.288; lastDigit = round(10*rem(num*1000,1)) % fourth digit right of the decimal point is zero
lastDigit =
0
>> num = 130404809.2882; lastDigit = round(10*rem(num*1000,1))
lastDigit =
2
But every once in a while, the finite nature of internal binary representation produces undesired results:
>> num = 136147309.434; lastDigit = round(10*rem(num*1000,1)) % should return zero
lastDigit =
10
I realize this is a common subtlety, and that my challenge is really not so much math as string parsing—but this is the data I have to work with.
Naively, I tried various combinations of floor, round, rem, etc. — but I haven't been able to find a clean way to extract that fourth digit that doesn't run into cases like the one above every so often. Can anyone set me straight?
3 个评论
Walter Roberson
2020-7-17
The correct last digit is not 1. The number is really 136147309.4339999854564666748046875 . You should floor() instead of round()
Steven Lord
2020-7-17
What are you doing that requires this digit? Perhaps there's an alternate way to achieve your ultimate goal that is more reliable.
采纳的回答
Stephen23
2020-7-17
编辑:Stephen23
2020-7-17
Some people will incorrectly tell you that this is not possible, but in reality there is a simple and efficient solution:
>> N = 130404809.288;
>> mod(round(10000*N),10)
ans = 0
>> N = 130404809.2882;
>> mod(round(10000*N),10)
ans = 2
>> N = 136147309.434;
>> mod(round(10000*N),10)
ans = 0
This relies on your statement that the values have "with either three or four digits to the right of the decimal point", i.e. that the fifth digit is zero (the sixth and any further digits, including any floating point error, are totally irrelevant). This means that after multiplying by 10000 (NOT by 1000 as you tried) a simple round will remove all floating point error from the number:
xyz.abcd0err % input
xyzabcd.0err % *10000
xyzabcd % round
d % mod 10
You were almost there, just off by a factor of ten.
0 个评论
更多回答(2 个)
Image Analyst
2020-7-17
Use sprintf() to round the number to a string with 4 digits, then extract the 4th digit after the decimal point and convert it to a number:
num = 130404809.288;
str = sprintf('%.4f', num)
decimalLocation = strfind(str, '.')
lastDigit = str2double(str(decimalLocation + 4))
num = 130404809.2882;
str = sprintf('%.4f', num)
decimalLocation = strfind(str, '.')
lastDigit = str2double(str(decimalLocation + 4))
num = 136147309.434;
str = sprintf('%.4f', num)
decimalLocation = strfind(str, '.')
lastDigit = str2double(str(decimalLocation + 4))
0 个评论
AMM
2020-7-17
编辑:AMM
2020-7-17
3 个评论
Image Analyst
2020-7-17
AMM - half a million elements is far from a large array. Processing them all shouldn't take much time. If you use sprintf() like I did, .4339999 will show up as .4340 and give you zero which is what I think you want.
Anyway, why do you need to do this quirky thing. What's the use case?
Walter Roberson
2020-7-17
readtable() / readmatrix() can handle fixed-width fields; see https://www.mathworks.com/help/matlab/ref/matlab.io.text.fixedwidthimportoptions.html
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Logical 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!