fasttime / JScrewIt

Write any JavaScript with six characters: ! ( ) + [ ]
https://jscrew.it
ISC License
161 stars 13 forks source link

Number encoding #9

Open 837951602 opened 7 years ago

837951602 commented 7 years ago

There should only be XXXeYYY and (AAA)(.BBB) -- since point costs 72 chars far larger than '9' 47, it's never cheap to use both. With x being a positive finite number, turning x to XXXeYYY style uses x.toExponential().replace(/^(\d)\.?(\d*)e(.*)$/,function(x,a,b,c){return a+b+'e'+(c-b.length)}) and to (AAA)(.BBB) use Number.prototype.forceFixed=function(){var t=this.toString().split('e');if(t.length==1)return t.replace(/^0/,'');t[0]=t[0].replace(/\./,'');return t[1]>0?t[0]+Array(2- -t[1]-t[0].length).join(0):'.'+Array(-t[1]).join('0')+t[0]}? (code not tested fully)

837951602 commented 7 years ago

It may be an idea to write -999999 as -1000000+1, and 1e12+1 to 1e12+1 but I don't know whether it's easy or difficult to achieve

837951602 commented 7 years ago

1.0000000000001e+113 +(+(+!![]+(!![]+[])[!![]+!![]+!![]]+(+!![])+(!![]+!![]+!![]))+(+!![])+(!![]+[])[!![]+!![]+!![]]+(+!![])+(+[])+(+[]))

fasttime commented 7 years ago

-999999 can be written as -1000000 + 1 and 1e12+1 as 1e12+1, that's fine because those are integers between Number.MAX_SAFE_INTEGER and -Number.MAX_SAFE_INTEGER. It wouldn't be too difficult either.

I'm not sure about 1.0000000000001e+113: this is a floating-point number that is only approximately equal to the sum of two powers of 10. Nothing in ECMAScript or IEEE-754 says that it couldn't be rounded to something slightly larger or slightly smaller, but most of all I don't know how consistent different engines are when calculating the sum of non-safe integers. JScrewIt must work with many different engines and I wouldn't be surprised if something that gives 1.000...0001 in my browser gave 1 in another one's browser. Long story short: this needs more experimentation and extensive testing if we don't want to break something accidentally.

837951602 commented 7 years ago

JavaScript Numbers are Always 64-bit Floating Point Unlike many other programming languages, JavaScript does not define different types of numbers, like integers, short, long, floating-point etc.

JavaScript numbers are always stored as double precision floating point numbers, following the international IEEE 754 standard.

This format stores numbers in 64 bits, where the number (the fraction) is stored in bits 0 to 51, the exponent in bits 52 to 62, and the sign in bit 63:

Value (aka Fraction/Mantissa) Exponent Sign 52 bits (0 - 51) 11 bits (52 - 62) 1 bit (63) Precision Integers (numbers without a period or exponent notation) are considered accurate up to 15 digits.

Example var x = 999999999999999; // x will be 999999999999999 var y = 9999999999999999; // y will be 10000000000000000 Try it Yourself » The maximum number of decimals is 17, but floating point arithmetic is not always 100% accurate:

Source: http://w3schools.bootcss.com/js/js_numbers.html

837951602 commented 7 years ago

I'm not sure about 1.0000000000001e+113: this is a floating-point number that is only approximately equal to the sum of two powers of 10.

The expression is not 1e113+1e100 but +((1e13+1)+'e100')

fasttime commented 7 years ago

@837951602 Ah, I see. But why not +(1e12+'1e100')? That could be implemented as a string cluster optimization, specifically for the case of a string containing a nonzero digit followed by many zeros.

837951602 commented 7 years ago

Yes +(1e12+'1e100') is a better choice yet +((1e10+1)+'e100') is shorter than +(1e9+'1e100')

837951602 commented 7 years ago

1e-292 +(+!![]+(!![]+[])[!![]+!![]+!![]]+(+((+(+!![]+[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+!![])+(+[]))+[])[+!![]]+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+!![]))+[])[!![]+!![]]+(!![]+!![])+(!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(!![]+!![])) 259c +(1+'e'+(10-302)) +(+!![]+(!![]+[])[!![]+!![]+!![]]+(+(+!![]+[+[]])+(+((+((+(+!![]+[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+!![])+(+[]))+[])[+!![]]+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+[])+(+!![]))+[])[!![]+!![]]+(!![]+!![]+!![])+(+[])+(!![]+!![]))))) 245c same type of -999999 as -1000000+1

837951602 commented 7 years ago

Not sure about if float values or exceeded_safe_integer calculated get the same value? If so, 0.9999999999999998(822c) can't be +('.'+(1e16-2))(323c) or 1-2e-16(339c) but only +('.'+(1e15-1)+8)(358c)

837951602 commented 7 years ago

So why is '-' translated into "1e-10"[2] instead of "1e-7"[2]? If because of compatible why no this feature

837951602 commented 7 years ago

1e-100 208c +(1e-10+[0]) 146c

fasttime commented 7 years ago

I don't know why "-" is defined how it is! This should be analyzed ASAP.

837951602 commented 7 years ago

(ECMA-262 5th edition December 2009, P48) http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262%205th%20edition%20December%202009.pdf 9.8.1 ToString Applied to the Number Type The abstract operation ToString converts a Number m to String format as follows:

  1. If m is NaN, return the String "NaN".
  2. If m is +0 or −0, return the String "0".
  3. If m is less than zero, return the String concatenation of the String "-" and ToString(−m).
  4. If m is infinity, return the String "Infinity".
  5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k , the Number value for s × 10n−k is m, and k is as small as possible. Note that k is the number of digits in the decimal representation of s, that s is not divisible by 10, and that the least significant digit of s is not necessarily uniquely determined by these criteria.
  6. If k ≤ n ≤ 21, return the String consisting of the k digits of the decimal representation of s (in order, with no leading zeroes), followed by n−k occurrences of the character ‘0’.
  7. If 0 < n ≤ 21, return the String consisting of the most significant n digits of the decimal representation of s, followed by a decimal point ‘.’, followed by the remaining k−n digits of the decimal representation of s.
  8. If −6 < n ≤ 0, return the String consisting of the character ‘0’, followed by a decimal point ‘.’, followed by −n occurrences of the character ‘0’, followed by the k digits of the decimal representation of s.
  9. Otherwise, if k = 1, return the String consisting of the single digit of s, followed by lowercase character ‘e’, followed by a plus sign ‘+’ or minus sign ‘−’ according to whether n−1 is positive or negative, followed by the decimal representation of the integer abs(n−1) (with no leading zeros).
  10. Return the String consisting of the most significant digit of the decimal representation of s, followed by a decimal point ‘.’, followed by the remaining k−1 digits of the decimal representation of s, followed by the lowercase character ‘e’, followed by a plus sign ‘+’ or minus sign ‘−’ according to whether n−1 is posi
837951602 commented 7 years ago

aemkei's jsfuck main page also said "0.0000001"->'1e-7' and get '-'. but creating it's 1e-10 https://github.com/aemkei/jsfuck/commit/4a5ff0e608fa4ac84dce3b7be70492ac9750ad0c

fasttime commented 7 years ago

Made a branch to test the "-" optimization: https://github.com/fasttime/JScrewIt/tree/minus-sign. Looks good so far. @837951602 Kudos for spotting this!

837951602 commented 5 years ago

9.999999999999997e+214 870c +'999999999999999.7e200' 865c

fasttime commented 5 years ago

9.999999999999997e+214 870c +'999999999999999.7e200' 865c

This is now done with 139479b314621ffb0c7065bfb7eadd0f00126aa1 in the update branch. Preview here.