libera/commonlisp - IRC Chatlog
Search
4:12:03
White_Flame
obviously, typing in 7.78 at the repl parses it as a single-float and prints it in the same 7.78 representation, so it's not a ieee 754 problem
4:12:30
beach
opcode: I haven't checked this particular case, but floats are not represented in decimal, so it is entirely possible that there is not an exact decimal representation for the input you are giving.
4:13:12
White_Flame
parse-float has a COERCE operation into the returned type, which is probably where the imprecision lies
4:13:27
opcode
look beach I can't tell if you're just trying to be irritating but I think you know what I meant. If you feed a parsing library a string with a number, it should give you that number. Not a different number. Unless you're suggesting SBCL can't represent the number "7.78"
4:14:02
opcode
White_Flame: thanks for actually helping, I was not aware of the read-default-float-format variable
4:14:35
beach
opcode: That's exactly what I am suggesting. Did you check that your input has an exact representation in IEEE?
4:14:45
beach
opcode: http://pages.cs.wisc.edu/~david/courses/cs552/S12/handouts/goldberg-floating-point.pdf
4:15:12
White_Flame
however, most FP systems try to ensure that the rounding involved makes the read input match the output print
4:15:59
opcode
but my point is that the result I get from the parse operation should be 7.78 because that's the string I fed in
4:17:02
opcode
beach: I don't know who you are but you've been profoundly unhelpful so I'm going to add you to ignore. Go troll someone else.
4:23:18
White_Flame
SBCL's reader appears to keep it as perfect rational, then coerces it into the final format
4:27:37
White_Flame
and COERCE goes through a lot of detail in the fp construction, with biases and whatnot, which matches its printer
4:31:45
opcode
I set read-default-float-format to 'double-float but it's still outputting the wrong number. Interestingly read-from-string does it correctly irrespective of float format
4:33:35
White_Flame
I don't think you're using the right terms in the right places, hence beach's objections
4:34:44
opcode
I'm well aware of the limitations of representing infinitely many numbers in a finite number of binary digits
4:35:14
beach
I think opcode had better adjust those expectations, but since I am being ignored, it doesn't matter much what I think.
4:35:53
White_Flame
different implementations' floating point storage (which might change with tagging strategies) and their printers could vary. Which one is "right"?
4:36:30
opcode
I don't think it's an extreme position to suggest that the "right" result is the one that common sense would indicate is correct
4:38:35
White_Flame
and again, 3rd party reader + 3 different implementations' printers, which should it match?
4:39:38
White_Flame
but even if it did, it doesn't know the implementation representation. It's only using toplevel math functions, not anything to do with the actual bit representation, as the implementation would do in its platform specific code
4:40:20
White_Flame
now, there's lots of work out there in terms of carefully matchign input to output, if you control both ends, but I'm not sure if those have a singular solution
4:43:42
opcode
Well, I have the most experience in C#/dotnet and in that ecosystem Double.Parse() will give you consistent results across the two platforms I've had time to try while we've been debating this
4:45:20
opcode
right but again, and this is just me personally, if I released a library that dealt with implementation-specific code and there was divergent behavior, irrespective of whether it's sockets or floats or what have you, I'd still consider that a bug
4:45:53
White_Flame
but this falls within reasonable realms of floating point issues, again because it's not targetting (and cannot target) a specific storage precision & printer
4:46:00
opcode
so when I came here asking about it, to be met with smug superiority from beach, it really rubs me the wrong way you know?
4:48:06
opcode
cmon man. 7.78 is not the same number (in the mathematical sense, not the binary representation sense) as 7.7799997
4:50:02
White_Flame
and as a library, as it really can't technically be done as such, has precision decisions that the printer doesn't expect
4:51:51
White_Flame
but still, this is veering really off topic for a pretty strictly on topic channel
5:18:31
beach
White_Flame: Since I am ignored, I can say that I don't think opcode was requesting matching input and output functions. I just think that opcode thinks that the input number has an exact representation in floating point and expect that exact representation to be output.
5:47:16
beach
If I am doing this right, the SBCL reader turns 7.78 into a number that is greater than 778/100, and I think SBCL has a good input algorithm, i.e. it creates the closest float possible. That would indicate that the parse-float library does not.
6:03:17
beach
This discussion reminds me of several incidents when we used Common Lisp in our teaching, and the students discovered that typing something like (+ 0.4 0.3) to the REPL doesn't give the answer 0.7. They were then very happy to announce that they had found a "bug in Common Lisp".
6:11:18
White_Flame
beach: yeah, parse-float coerces portions of the number to the target float type, then combines them with adds and multiplies. this compounds precision issues
6:23:40
jasom
However it's not unusual to see string -> float conversions with an error greater than 1ULP
6:28:23
lisp123
beach: You mentioned earlier one of the issues with a CL editor being that without multiple global environments, a crash in the code will cause the editor to crash. How did Lisp Machines or Hemlock (and others) handle this?
6:28:43
beach
White_Flame: Strange! If I wrote such a thing, I would make sure to use one of the known algorithms for creating the closest possible float.
6:31:53
beach
lisp123: I don't know. The issue is that current Common Lisp implementations prioritize performance over safety. I suppose the Lisp Machine didn't have to do that since they had a lot of hardware support. Don't know about CMUCL (which I believe Hemlock was written for), but it is entirely possible that SBCL has changed its priorities so that performance is more important. But i am just guessing.
6:33:40
beach
lisp123: But it's not related to multiple global environments like that. I am saying that, with multiple global environments done right, you wouldn't have a disaster if you (say) FMAKUNBOUND CAR. You would just consider that environment dead and create a new default one.
6:34:09
beach
lisp123: And you would not run the editor in the same environment as you work, so you would kill your work environment, but not the editor.
6:35:53
jackdaniel
I think that asserting that current implementations are more concerned about performance than safety may be a bit a stretch. if climacs signals error that is handler nowhere, you will end up in a debugger no matter how much attention you put to safety
6:36:49
rotateq
often also too much business, unnecessary and inefficient planning and too less mathematics
6:37:33
beach
jackdaniel: Right. The scenario I imagined was not when there is a defect in the editor, but when the editor is used in the same image as the application being written, and the programmer does something unsafe.
6:37:36
jackdaniel
opcode: if you expect exact representation of numbers in common lisp you may consider using ratios instead of floats
6:38:47
lisp123
I may ask this question on Stack Overflow. Because I'm curious that without being in the same image, it is not a trivial affair to transfer Lisp data to / from the image and capture all scenarios
6:41:51
beach
lisp123: But people seem to accept those limitations to avoid the scenario of an application crashing the editor. Shinmera, for instance, once said something like "I will never use an editor that runs in the same image as my application code". It is not an exact quote, but similar in spirit. And I can understand him, given how relatively easy it is to crash the Common Lisp implementation.
6:41:57
jackdaniel
beach: so you mean that some unrelated application sets (safety 0) (speed 3) and triggers some undefined behavior? I would classify it as the programmer being more concerned about performance than safety - the is neutral with this regard (i.e doesn't decide either way)
6:43:07
beach
jackdaniel: I am told that SBCL believes the programmer when a DYNAMIC-EXTENT declaration is used.
6:43:44
lisp123
beach: Yes. Which makes me wonder what was done in the past. One _speculation_ I have is that they had a more advanced version of SWANK, but hopefully one of the lisp machine experts can answer it
6:44:05
beach
But, sure, it is quite possible to state that it is unwise to do things like that with an editor that runs in the same image as the application being developed.
6:44:12
jackdaniel
OK, I see your point; I think that the operator `the` is a good counter-example -- sbcl does trust the when safety is <=1 I think, while it doesn't on default settings
6:44:57
lisp123
(theoretically I don't see an elegant solution without multiple global environments)
6:46:45
jackdaniel
n.b (not an argument but an observation), sbcl has ext:*stack-allocate-dynamic-extent* to make it less trusting :) (i.e not trusting unless proven)
6:59:36
dickbar__
Just a remark: "7.78" should be printed as 7.78000000... (infinit number of zero's). You wil hear from me when i'am ready checking:-)
7:04:09
jackdaniel
"7.78" as a string should be printed as "7.78" or, aesthetically, as 7.78 - but it is not a number ;)
7:22:52
saltrocklamp[m]
does anyone here use the `parse-number` library? it seems to triggers some kind of deep internal error in both ccl and sbcl when attempting to parse a string with a literal tab character in it. e.g. in ccl i get "Error: The value NIL is not of the expected type UNSIGNED-BYTE. While executing: (:INTERNAL CCL::BAD-SEQUENCE-INTERVAL CCL::CHECK-SEQUENCE-BOUNDS)"
8:26:12
JeromeLon
oh no, I missed one of my favorite descussions on binary numbers. I think a good answer was: 7.78 is 111.1100011110101110000101000111101011100001010001111010111000010100011110101110000101000111101011100001... in binary, which is truncated as specified by IEEE754 single precision to 111.110001111010111000010, which is exactly 7.7799997 in decimal.
8:30:47
beach
JeromeLon: But the next higher single float is closer to 7.78 than the truncated one I think. So that means that PARSE-FLOAT truncates rather than returning the best approximation. Right?
8:32:25
jackdaniel
if ieee754 specifies truncating then it should truncate (well, not that common lisp tandard stipulates that its floats behave exactly like ieee754)
8:33:00
jackdaniel
marcoxa works on a spec for common lisp floats, it was mentioned during last (or one before last) els
8:33:11
beach
Does IEEE745 specify that it should truncate when a decimal representation is converted to a single float?
8:33:41
jackdaniel
I don't know, I'm repeating what JeromeLon said "which is truncated as specified by IEEE754 single precision"
8:34:58
beach
Whereas there are papers published on how to obtain the closest float for a particular decimal representation.
8:36:04
JeromeLon
IEEE745 specifies several rounding methods. Does it recommend one of them? I don't know actually
8:36:53
beach
JeromeLon: There is no reason for PARSE-FLOAT to generate the original value that you showed.
8:37:02
JeromeLon
beach: yes, I agree with you, OC should ponder the source of the number 7.78. If it's a currency, it should not be parsed as a float.
8:38:00
beach
The best action on the part of PARSE-FLOAT would, in my opinion, be to return the best approximation. Not the truncation.
8:39:10
beach
And this opinion has nothing to do with IEEE truncation or rounding, nor anything about currency. Just how PARSE-FLOAT has decided to do it.
8:41:45
JeromeLon
beach: I agree that the best approximation would be more correct. I disagree that it has nothing to do with IEEE 754 truncation or rounding. If IEEE 754 specifies clearly which rounding should be done, that PARSE-FLOAT should just do that.
8:45:34
beach
JeromeLon: Where does IEEE say what rounding should be used when a decimal representation is converted to an IEEE float?
8:46:29
beach
Notice "when a decimal representation is converted", not "when a higher-precision binary representation is converted".
8:49:55
JeromeLon
beach: from wikipedia: "754-2008 requires correctly rounded base conversion between decimal and binary floating point within a range which depends on the format"
8:51:31
JeromeLon
I agree. And it means that whatever 754 defines as the correct rounding is what PARSE-FLOAT should be doing
8:58:13
beach
As far as I can tell, IEEE rounding has to do with the result of operations between floating-point numbers.
9:01:55
beach
The IEEE standard says "they shall use correct rounding" for the conversion, which sounds to me like "they shall return the closest floating-point number".
9:03:13
_death
ieee754-2008 has a section 5.12 Details of conversion between floating-point data and external character sequences.. I'm not an expert on floats, but skimming it they talk about "correct rounding" which 2.1.12 defines as the rounding determined by the applicable rounding direction
9:06:38
beach
Oh, but that's only valid when the result is exactly in the middle between two values.
9:09:11
beach
So if the next higher floating-point number is strictly closer to 7.78, then that next higher number should be returned.
9:11:27
_death
the definition of roundTiesToEven does not refer only to ties, it includes the notion of nearest
9:13:01
_death
JeromeLon: btw duckduckgo gave me https://irem.univ-reunion.fr/IMG/pdf/ieee-754-2008.pdf
9:46:29
lisp123
Is there a curated list of hall of fame posts from c.l.l (information / discussion)?
9:49:26
lisp123
flip214: I was thinking more broadly. I read it from time to time and there's a lot of wisdom in there
9:50:20
_death
there are too many.. just get the c.l.l archive and a good news reader (say gnus), sit back, relax, and spend a couple of months getting up to date
9:53:14
lisp123
I'm also trying to download all of CMU AI Repository at the moment, will put it on GitHub once done
9:56:51
JeromeLon
Sorry, I should have been clearer: the next single float was closer to the correct result
9:57:43
JeromeLon
PARSE-FLOAT is doing (+ 7.0 0.78) as its last step, loosing accuracy in the addition
9:59:32
JeromeLon
7.7800002 is closer than 7.7799997, but maybe that's how IEEE 745 addition is specified?
10:03:58
pjb
JeromeLon: (loop for *read-default-float-format* in '(short-float single-float double-float long-float) collect (+ 7 (read-from-string "0.78"))) #| --> (7.7799997 7.7799997 7.78D0 7.78D0) |#
10:04:40
pjb
JeromeLon: if you're into high precision floating point computations, you should put (setf *read-default-float-format* 'double-float) in your rc file.
10:04:46
beach
JeromeLon: And it is entirely possible that the addition is rounded correctly while still giving that result.
10:14:55
JeromeLon
Also, in C, the float addition result in rounding above: https://onlinegdb.com/pLSxkTMR-o
10:16:29
pjb
The point is that 7.0 = 111000000000000000000000e-21 0.78 = 111110001111010111000011e-24 = 000110001111010111000010e-21 and 7.0 + 0.78 = 111110001111010111000010e-21 = 7.7799997
10:17:26
_death
JeromeLon: you know the single float is converted to double when passed to printf right?
10:21:23
JeromeLon
pjb: "000110001111010111000010e-21" this looks truncated instead of rounded. 000110001111010111000011e-21 is closer
10:22:11
_death
JeromeLon: but you didn't give it 0.78, which is not representable as a floating point value
10:23:34
JeromeLon
_death: 0.78 can be represented as either .07799997 or 0.07800002, I believe the debate is about which one is more correct.
10:25:54
JeromeLon
0.78 can only be represented as 0.7799997 or 0.7800002 when we are missing 3 digits in single float.
10:28:55
_death
would it be fair to say that you want to ask why 0.78 gets converted to 0.7799997 and not 0.7800002
10:31:20
JeromeLon
_death: no (because it's not converted to that). I'll do a nice diagram and post back.
10:42:14
JeromeLon
Legent: when summing 7 0.779999971389770507812500 (which is the single-float representation of 0.78), the 2 possible roundings (7.779999732971191406250 and 7.780000209808349609375) have the same distance to non-rounded value.
10:45:37
_death
I didn't check the actual float value 0.78 gets converted to; only took the values discussed
10:49:50
JeromeLon
so the conclusion is that PARSE-FLOAT doing a sum (integer part + decimal part) can introduce an error when the rounding of the decimal part changes in turn the rounding of the sum.
10:55:03
_death
(loop for x in (list (parse-float:parse-float "0.78") (+ 7.0 0.78) 0.78) collect (decimals:format-decimal-number x :round-magnitude -24))
12:20:49
lisp123
Actually on that, can I compare two uninterned symbols for equality via (equal (symbol-name #:me) (symbol-name #:you))?
12:23:53
beach
The names are strings, so you can use whatever comparison on strings that works for you.
13:01:48
saltrocklamp[m]
<_death> "salt rock lamp: it's not bummer,..." <- that was quick! you found the issue?
13:02:49
saltrocklamp[m]
i see, very small fix: https://github.com/sharplispers/parse-number/pull/10
13:03:59
saltrocklamp[m]
has anyone here used `check-it` before https://github.com/DalekBaldwin/check-it? something like `parse-number` would be a great place for property-based testing
15:28:16
saltrocklamp[m]
https://bpa.st/LRTQ does anyone see an obvious reason why the lisp version of my code (with sbcl) is not only ~5x slower than the python version of my code, but also isn't giving the right answers? the correct output numbers should be something like `249.9008` and `288.6628`
15:32:30
Bike
the latter of which you could deal with by reusing a preallocating string instead of allocating a new one each time read-line is called; maybe python is smart enough to do that for the "for line in" construct
15:37:25
Krystof
I think python defaults to double float; SBCL definitely defaults to single-float. That might be enough to explain the different answers
15:39:24
Krystof
well, actually: your wrapper around parse-number returns (values 0.0 nil) on a parse failure, but your check tests for the primary value being null
15:40:37
saltrocklamp[m]
that said, i had a previous version of this that used READ + TYPEP to "parse" floats (returning NIL if it didn't actually read a float), and i think the answer was wrong there too, even when i set *READ-DEFAULT-FLOAT-FORMAT* to double-float
15:43:46
saltrocklamp[m]
and i will try re-using the string, i assume you mean i should `setq`/`setf` it instead of using a step-form in `do`?