Extracting a specific digit from a float w/o floating-point implementation rounding issues
Ältere Kommentare anzeigen
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 Kommentare
John D'Errico
am 17 Jul. 2020
There is no reasonable way to do what you ask, to get the perfectly correct result every time. This is the nature of floating point arithmetic.
I said "reasonable", because in theory you could always set up a lookup table, for EVERY floaing point number representable by a double. Decide what you think the result should be for each and every such number. Since there would be 2^64 distinct numbers representable by a double, that will be a rather large lookup table.
Walter Roberson
am 17 Jul. 2020
The correct last digit is not 1. The number is really 136147309.4339999854564666748046875 . You should floor() instead of round()
Steven Lord
am 17 Jul. 2020
What are you doing that requires this digit? Perhaps there's an alternate way to achieve your ultimate goal that is more reliable.
Akzeptierte Antwort
Weitere Antworten (2)
Image Analyst
am 17 Jul. 2020
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))
3 Kommentare
Stephen23
am 17 Jul. 2020
"...except that I have no fifth digit: it's not zero, it's just not there—so I suppose I can simply treat it as zero."
Binary floating point numbers have a fixed number of fractional digits, so in fact your values do have 5th, 6th, etc. decimal digits, even if those decimal digits are not displayed by whatever format or viewer that you are using. Binary floating point numbers do not change precision just because some of the (approximately equivalent decimal) digits happen to be zero: those digits might not be displayed, but they are certainly stored in memory.
Image Analyst
am 17 Jul. 2020
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
am 17 Jul. 2020
readtable() / readmatrix() can handle fixed-width fields; see https://www.mathworks.com/help/matlab/ref/matlab.io.text.fixedwidthimportoptions.html
Kategorien
Mehr zu Logical finden Sie in Hilfe-Center und File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!