Last active
May 4, 2020 18:19
-
-
Save renatoathaydes/e015019c4283d6a54903a49d2887f59f to your computer and use it in GitHub Desktop.
Calculating a number from its constituent parts (encoded as ASCII bytes)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.nio.charset.StandardCharsets; | |
class JavaComparison | |
{ | |
public static void main(String[] args) | |
{ | |
System.out.println("CUSTOM"); | |
for (int i = 0; i < 100; i++) | |
{ | |
custom(); | |
} | |
System.out.println("JAVA"); | |
for (int i = 0; i < 100; i++) | |
{ | |
java(); | |
} | |
System.out.println("CUSTOM"); | |
for (int i = 0; i < 10; i++) | |
{ | |
custom(); | |
} | |
System.out.println("JAVA"); | |
for (int i = 0; i < 10; i++) | |
{ | |
java(); | |
} | |
} | |
public static void java() | |
{ | |
var s = "34.890625e10"; | |
var b = s.getBytes(StandardCharsets.US_ASCII); | |
var r = System.nanoTime(); | |
var result = Float.parseFloat(new String(b, StandardCharsets.US_ASCII)); | |
var t = System.nanoTime() - r; | |
System.out.println("RESULT: " + result + "(took " + t + "ns)"); | |
} | |
public static void custom() | |
{ | |
byte[] bytes = bytes("34"); | |
byte[] bytes1 = bytes("890625"); | |
byte[] bytes2 = bytes("10"); | |
var s = System.nanoTime(); | |
var n = parseInt(bytes); | |
var f = parseFraction(bytes1); | |
var e = parseInt(bytes2); | |
var m = Math.pow(10, e); | |
var result = n * m + f * m; | |
var t = System.nanoTime() - s; | |
System.out.println("RESULT: " + result + "(took " + t + "ns)"); | |
} | |
private static byte[] bytes(String s) | |
{ | |
var b = s.getBytes(StandardCharsets.US_ASCII); | |
for (int i = 0; i < b.length; i++) | |
{ | |
b[i] = (byte) (b[i] - 48); | |
} | |
return b; | |
} | |
private static int parseInt(byte[] bs) | |
{ | |
int current = 0; | |
for (int i = 0; i < bs.length; i++) | |
{ | |
var mult = (int) Math.pow(10, bs.length - i - 1); | |
var n = (bs[i] * mult); | |
current += n; | |
} | |
return current; | |
} | |
private static float parseFraction(byte[] bs) | |
{ | |
float current = 0f; | |
for (int i = 0; i < bs.length; i++) | |
{ | |
var mult = (float) Math.pow(10, -i - 1); | |
var n = (bs[i] * mult); | |
current += n; | |
} | |
return current; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.nio.charset.StandardCharsets; | |
class Scratch | |
{ | |
public static void main(String[] args) | |
{ | |
var n = parseInt(bytes("34")); | |
var f = parseFraction(bytes("890625")); | |
var e = parseInt(bytes("10")); | |
var m = Math.pow(10, e); | |
var result = n * m + f * m; | |
System.out.println("RESULT: " + result); | |
} | |
private static byte[] bytes(String s) | |
{ | |
var b = s.getBytes(StandardCharsets.US_ASCII); | |
for (int i = 0; i < b.length; i++) | |
{ | |
b[i] = (byte) (b[i] - 48); | |
} | |
return b; | |
} | |
private static int parseInt(byte[] bs) | |
{ | |
int current = 0; | |
for (int i = 0; i < bs.length; i++) | |
{ | |
var mult = (int) Math.pow(10, bs.length - i - 1); | |
var n = (bs[i] * mult); | |
System.out.println(n); | |
current += n; | |
} | |
return current; | |
} | |
private static float parseFraction(byte[] bs) | |
{ | |
float current = 0f; | |
for (int i = 0; i < bs.length; i++) | |
{ | |
var mult = (float) Math.pow(10, -i - 1); | |
var n = (bs[i] * mult); | |
System.out.println(n); | |
current += n; | |
} | |
return current; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The above shows an approximation algorithm for parsing floating-point numbers once its component parts are known, as parsed from a stream of US_ASCII bytes (or UTF-8 if already validated, as the bytes would be the same).
That's what the above code simulates.
In my machine, the last twenty runs (after 100 runs for each algorithm to warm up the JVM) show that the custom algorithm is around 10x times faster than using the lazy approach with the Java built-in parser (which, to be fair, uses a more accurate algorithm, but which is unlikely to matter in practice).