Showing posts with label Go. Show all posts
Showing posts with label Go. Show all posts

Tuesday 19 January 2016

Floating point arithmetic with decimals

G'day:
As a human... what is the value of z, after you process this pseudocode with your wetware:

x = 17.76
y = 100
z = x * y

Hopefully you'd say "1776". It was not a trick question.

And that's an integer, right? Correct.

CFML

Now... try this CFML code:

x = 17.76;
y  = 100;
z = x*y;

writeOutput(z);

1776 So far so good.

But what about this:

writeOutput(isValid("integer", z));

You might think "YES" (or true if yer on Lucee), however it's "NO".

And this is where young players fall into the trap. They get all annoyed with isValid() getting it wrong, etc. Which, to be fair, is a reasonable assumption with isValid(), but it's not correct in this instance. It's the young player who is mistaken.

If we now do this:

writeOutput(z.getClass().getName());

We get: java.lang.Double

OK, but 1776 can be a Double, sure. But CFML should still consider a Double 1776 as a valid integer, as it should be able to be treated like one. So why doesn't it? What if we circumvent CFML, and go straight to Java:

writeOutput(z.toString());

1776.0000000000002

Boom. Floating point arithmetic inaccuracy.

Never ever ever forget, everyone... when you multiply floating point numbers with decimals... you will get "unexpected" (but you should pretty much expect it!) floating point accuracy issues. This is for the perennial reason that what's easy for us to express in decimal is actually quite hard for a computer to translate into binary accurately.

Aside: we were chatting about all this on the CFML Slack channel this morning, and one person asked "OK, so how come 17.75 x 100 works and 17.76 x 100 does not?". This is because a computer can represent 0.75 in binary exactly (2-1 + 2-2), whereas 0.76 can only be approximated, hence causing the "issue".

The problem really is that CFML should simply output 1776.0000000000002 when we ask it, and it should not try to be clever and hide this stuff. Because it's significant information. Then when the young player output the value, they'd go "oh yeah, better round that" or whatever they need to do before proceeding. CFML is not helping here.

This is pretty ubiquitous in programming. Let's have a trawl through the various languages I can write the simplest of code in:

JavaScript


x = 17.76;
y = 100;
z = x * y

console.log(z);


>node jsVersion.js
1776.0000000000002

>

JS just does what it's told. Unsurprisingly.

Groovy


x = 17.76
y = 100
z = x * y
println "x * y: " + z

println "x: " + x.getClass().getName()
println "y: " + y.getClass().getName()
println "z: " + z.getClass().getName()
println "z: " + z.toString()


>groovy32 groovyVersion.groovy

x * y: 1776.00
x: java.math.BigDecimal
y: java.lang.Integer
z: java.math.BigDecimal
z: 1776.00
>


This is interesting. Whilst Groovy keeps the result as a float (specifically a BigDecimal) - which is correct - it truncates it to the total number of decimal places expressed in its factors. That's how I was taught to do it in Physics at school, so I like this. This second example makes it more clear:

x = 3.30
y = 7.70
z = x * y
println "x * y: " + z

println "x: " + x.getClass().getName()
println "y: " + y.getClass().getName()
println "z: " + z.getClass().getName()
println "z: " + z.toString()


>groovy32 more.groovy
x * y: 25.4100
x: java.math.BigDecimal
y: java.math.BigDecimal
z: java.math.BigDecimal
z: 25.4100
>

In 3.30 and 7.70 there are four decimal places expressed (ie: two for each factor), so Groovy maintains that accuracy. Nice!


Java


import java.math.BigDecimal;

class JavaVersion {

    public static void main(String[] args){
        double x = 17.76;
        int y = 100;
        System.out.println(x*y);
        
        BigDecimal x2 = new BigDecimal(17.76);
        BigDecimal y2 = new BigDecimal(100);
        System.out.println(x2.multiply(y2));
        
    }
}

Here I added a different variation because I was trying to see why the Groovy code behaved the way it did, but it didn't answer my question. I suspected that perhaps it was a BigDecimal thing how it decided on the accuracy of the result, but it wasn't:


>java JavaVersion
1776.0000000000002
1776.000000000000156319401867222040891647338867187500

>

This is a good demonstration of how a simply base-10 decimal fraction is actually an irrational number in binary.

Monday 22 December 2014

Weekend code puzzle: my answer (Go version)

G'day:
I sat down on Saturday / Sunday and taught myself enough Go to be able to answer the puzzle question from "Something for the weekend? A wee code puzzle (in CFML, PHP, anything really...)". I've certainly got a lot of mileage out of that one topic, eh? Oh well: I think it's a good baseline exercise for researchng new languages, and I like seeing other people's code, too. I'm so pleased I got such a good range of submissions. However it does mean it's taking me an age to slog through them all.

Right, so I discussed Adam Presley's two Go entries over the last few days: "Weekend puzzle: Adam Presley's answer (Go)" and "Weekend puzzle: Adam Presley's answer - redux (Go)", and I did a "G'day World in Go" exercise in prep for understanding his code. Not that a "G'day world" exercise was sufficient for that, but at least I got Go installed and compiling, and located the docs etc.

Sunday 21 December 2014

Weekend code puzzle: Adam Presley's answer - redux (Go)

G'day:
Adam Presley is a bit of a star. I had a look at his original entry to the code puzzle and decided I didn't like it as it was too long-winded ("Weekend code puzzle: Adam Presley's answer (Go)"). He's subsequently come back to me with a simplified version, listed on his own blog: Response To Adam Cameron's Code Review. He took my comments in good grace, which is very good of him.

Let's have a look at this new version:

Wednesday 17 December 2014

Weekend code puzzle: Adam Presley's answer (Go)

G'day:
At long last, I'm continuing to look at each person's submissions for the code puzzle ("Something for the weekend? A wee code puzzle (in CFML, PHP, anything really...)"). There's been a bit of a hiatus because I've been busy with other things, plus generally not that interested in keeping up with this blog. Oops.

OK, so Adam's effort is in Go. The github repo for it can be found here: https://github.com/adampresley/adamCameronCodeChallenge201411.

G'day World in Go

G'day:
I've got an odd situation at present... I have a suspected lump on the inside of my eyeball, underneath my retina. I say "suspected" because the symptoms I have are those that I've had before with my other eye, and "a lump" was what it turned out to be. The manifestation of this lump is that I have a "dull" patch in my vision, caused by the light hitting the wrong part of my retina where the lump is, and my brain not quite being able to adjust for it. I'm in the NHS waiting list to get it seen to, but that's not until Dec 31. Last time the treatment was some pills, so I hope it's the same again, rather than it being like something from Un Chien Andalou. Anyway... I'm telling you this because currently I am off work due to only being able to look at a screen for a few minutes at a time, before my head starts hurting. The dull patch is to the immediate right of my focus when reading, meaning the word following the one I am currently reading is "greyed-out". Frustrating as f***.

I'm finding this jolly tedious, have decided to try to write something today, to check how long it is before I need to give up. Hopefully it'll also give my brain some training to work around the dull patch. In my vision, I mean.

So - other than the fact I'm already at the 250-word mark with completely off-topic nonsense (what is this? CFHour? ;-) - here's a short article on my experiences of getting Go up and running on this box. it's very straight forward, and this article really only serves to demonstrate how straight fwd it is, so as to perhaps give people encouragement to try it out.