Convert partial number to written word form in Google Sheets (e.g. 1300 to 1.3 thousand)? [duplicate] - google-sheets

is there a way how to custom format ridiculously large numbers (at least up to 10^100 in both ways) in google sheets:
thousands > K
millions > M
billions > B
trillions > T
etc...
negative quadrillions > Q
decillions > D
either via:
internal custom number formatting
formula (array formula ofc)
script similar to this one just extended to cover more ground
10000.1 10.0K
100 100.0
1000 1.0K
10000 10.0K
-100000 -100.0K
45646454 45.6M
5654894844216 5.7T
4655454544 4.7B
46546465455511 46.5T
-46546465455511 -46.5T
4654646545551184854556546454454400000000000000000000000000010000000 4.7U
-1000.9999 -1.0K
-100.8989 -100.9
-20.354 -20.4
1.03 1.0
22E+32 2.2D

internal custom number formatting solution:
sadly, the internal formatting in google sheets is by default able to work with only 3 types of numbers:
positive (1, 2, 5, 10, ...)
negative (-3, -9, -7, ...)
zero (0)
this can be tweaked to show custom formatting like thousands K, millions M and regular small numbers:
[>999999]0.0,,"M";[>999]0.0,"K";0
or only thousands K, millions M, billions B
[<999950]0.0,"K";[<999950000]0.0,,"M";0.0,,,"B"
or only negative thousands K, negative millions M, negative billions B
[>-999950]0.0,"K";[>-999950000]0.0,,"M";0.0,,,"B"
or only millions M, billions B, trillions T:
[<999950000]0.0,,"M";[<999950000000]0.0,,,"B";0.0,,,,"T"
or only numbers from negative million M to positive million M:
[>=999950]0.0,,"M";[<=-999950]0.0,,"M";0.0,"K"
but you always got only 3 slots you can use, meaning that you can't have trillions as the 4th type/slot. fyi, the 4th slot exists, but it's reserved for text. to learn more about internal formatting in google sheets see:
https://developers.google.com/sheets/api/guides/formats#meta_instructions
https://www.benlcollins.com/spreadsheets/google-sheets-custom-number-format/
formula (array formula) solution:
the formula approach is more versatile... first, you will need to decide on the system/standard you want to use (American, European, Greek, International, Unofficial, etc...):
en.wikipedia.org/wiki/Names_of_large_numbers
en.wikipedia.org/wiki/Metric_prefix
simple.wikipedia.org/wiki/Names_for_large_numbers
home.kpn.nl/vanadovv/BignumbyN
after that try:
=INDEX(REGEXREPLACE(IFNA(TEXT(A:A/10^(VLOOKUP(LEN(TEXT(INT(ABS(A:A)), "0"))-1,
SEQUENCE(35, 1,, 3), 1, 1)), "#.0")&VLOOKUP(ABS(A:A)*1, {{10^SEQUENCE(34, 1, 3, 3)},
{"K "; "M "; "B "; "T "; "Qa "; "Qi "; "Sx "; "Sp "; "O "; "N "; "D "; "Ud ";
"Dd "; "Td "; "Qad"; "Qid"; "Sxd"; "Spd"; "Od "; "Nd "; "V "; "Uv "; "Dv "; "Tv ";
"Qav"; "Qiv"; "Sxv"; "Spv"; "Ov "; "Nv "; "Tr "; "Ut "; "Dt "; "Tt "}}, 2, 1),
IF(ISBLANK(A:A),, TEXT(A:A, "0.0 "))), "^0\.0 $", "0 "))
works with positive numbers
works with negative numbers
works with zero
works with decimal numbers
works with numeric values
works with plain text numbers
works with scientific notations
works with blank cells
works up to googol 10^104 in both ways
extra points if you are interested in how it works...
let's start with virtual array {{},{}}. SEQUENCE(34, 1, 3, 3) will give us 34 numbers in 1 column starting from number 3 with the step of 3 numbers:
these will be used as exponents while rising 10 on the power ^
so our virtual array will be:
next, we insert it as the 2nd argument of VLOOKUP where we check ABS absolute values (converting negative values into positive) of A column multiplied by *1 just in case values of A column are not numeric. via VLOOKUP we return the second 2 column and as the 4th argument, we use approximate mode 1
numbers from -999 to 999 will intentionally error out at this point so we could later use IFNA to "fix" our errors with IF(A:A=IF(,,),, TEXT(A:A, "#.0 ")) translated as: if range A:A is truly empty ISBLANK output nothing, else format A column with provided pattern #.0 eg. if cell A5 = empty, the output will be blank cell... if -999 < A5=50 < 999 the output will be 50.0
and the last part:
TEXT(A:A/10^(VLOOKUP(LEN(TEXT(INT(ABS(A:A)), "0"))-1,
SEQUENCE(35, 1,, 3), 1, 1)), "#.0")
ABS(A:A) to convert negative numbers into positive. INT to remove decimal numbers if any. TEXT(, "0") to convert scientific notations 3E+8 into regular numbers 300000000. LEN to count digits. -1 to correct for base10 notation. VLOOKUP above-constructed number in SEQUENCE of 35 numbers in 1 column, this time starting from number 0 ,, with the step of 3 numbers. return via VLOOKUP the first 1 column (eg. the sequence) in approximate mode 1 of vlookup. insert this number as exponent when rising the 10 on power ^. and take values in A column and divide it by the above-constructed number 10 raised on the power ^ of a specific exponent. and lastly, format it with TEXT as #.0
to convert ugly 0.0 into beautiful 0 we just use REGEXREPLACE. and INDEX is used instead of the longer ARRAYFORMULA.
sidenote: to remove trailing spaces (which are there to add nice alignment lol) either remove them from the formula or use TRIM right after INDEX.
script solution:
gratitude to #TheMaster for covering this
here is a mod of it:
/**
* formats various numbers according to the provided short format
* #customfunction
* #param {A1:C100} range a 2D array
* #param {[X1:Y10]} database [optional] a real/virtual 2D array
* where the odd column holds exponent of base 10
* and the even column contains format suffixes
* #param {[5]} value [optional] fix suffix to fixed length
* by padding spaces (only if the second parameter exists)
*/
// examples:
// =CSF(A1:A)
// =CSF(2:2; X5:Y10)
// =CSF(A1:3; G10:J30)
// =CSF(C:C; X:Y; 2) to use custom alignment
// =CSF(C:C; X:Y; 0) to remove alignment
// =INDEX(TRIM(CSF(A:A))) to remove alignment
// =CSF(B10:D30; {3\ "K"; 4\ "TK"}) for non-english sheets
// =CSF(E5, {2, "deci"; 3, "kilo"}) for english sheets
// =INDEX(IF(ISERR(A:A*1); A:A; CSF(A:A))) to return non-numbers
// =INDEX(IF((ISERR(A:A*1))+(ISBLANK(A:A)), A:A, CSF(A:A*1))) enforce mode
function CSF(
range,
database = [
[3, 'K' ], //Thousand
[6, 'M' ], //Million
[9, 'B' ], //Billion
[12, 'T' ], //Trillion
[15, 'Qa' ], //Quadrillion
[18, 'Qi' ], //Quintillion
[21, 'Sx' ], //Sextillion
[24, 'Sp' ], //Septillion
[27, 'O' ], //Octillion
[30, 'N' ], //Nonillion
[33, 'D' ], //Decillion
[36, 'Ud' ], //Undecillion
[39, 'Dd' ], //Duodecillion
[42, 'Td' ], //Tredecillion
[45, 'Qad'], //Quattuordecillion
[48, 'Qid'], //Quindecillion
[51, 'Sxd'], //Sexdecillion
[54, 'Spd'], //Septendecillion
[57, 'Od' ], //Octodecillion
[60, 'Nd' ], //Novemdecillion
[63, 'V' ], //Vigintillion
[66, 'Uv' ], //Unvigintillion
[69, 'Dv' ], //Duovigintillion
[72, 'Tv' ], //Trevigintillion
[75, 'Qav'], //Quattuorvigintillion
[78, 'Qiv'], //Quinvigintillion
[81, 'Sxv'], //Sexvigintillion
[84, 'Spv'], //Septenvigintillion
[87, 'Ov' ], //Octovigintillion
[90, 'Nv' ], //Novemvigintillion
[93, 'Tr' ], //Trigintillion
[96, 'Ut' ], //Untrigintillion
[99, 'Dt' ], //Duotrigintillion
[100, 'G' ], //Googol
[102, 'Tt' ], //Tretrigintillion or One Hundred Googol
],
value = 3
) {
if (
database[database.length - 1] &&
database[database.length - 1][0] !== 0
) {
database = database.reverse();
database.push([0, '']);
}
const addSuffix = num => {
const pad3 = (str = '') => str.padEnd(value, ' ');
const decim = 1 // round to decimal places
const separ = 0 // separate number and suffix
const anum = Math.abs(num);
if (num === 0)
return '0' + ' ' + ' '.repeat(separ) + ' '.repeat(decim) + pad3();
if (anum > 0 && anum < 1)
return String(num.toFixed(decim)) + ' '.repeat(separ) + pad3();
for (const [exp, suffix] of database) {
if (anum >= Math.pow(10, exp))
return `${(num / Math.pow(10, exp)).toFixed(decim)
}${' '.repeat(separ) + pad3(suffix)}`;
}
};
return customFunctionRecurse_(
range, CSF, addSuffix, database, value, true
);
}
function customFunctionRecurse_(
array, mainFunc, subFunc, ...extraArgToMainFunc
) {
if (Array.isArray(array))
return array.map(e => mainFunc(e, ...extraArgToMainFunc));
else return subFunc(array);
}
sidenote 1: this script does not need to be authorized priorly to usage
sidenote 2: cell formatting needs to be set to Automatic or Number otherwise use enforce mode
extra:
convert numbers into plain text strings/words
convert array of numbers into plain text strings/words
convert custom formatted numbers into numeric numbers/values
convert text string datetime into duration value
convert text string formatted numbers into duration
convert your age into years-months-days

For almost all practical purposes we can use Intl compact format to achieve this functionality.
/**
* Utility function needed to recurse 2D arrays
*/
function customFunctionRecurse_(
array,
mainFunc,
subFunc,
...extraArgToMainFunc
) {
if (Array.isArray(array))
return array.map(e => mainFunc(e, ...extraArgToMainFunc));
else return subFunc(array);
}
/**
* Simple custom formating function using Intl
* #see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
* #customfunction
* #author TheMaster https://stackoverflow.com/users/8404453
* #param {A1:D2} numArr A 2D array
* #returns {String[][]}Compact Intl formatted 2D array
*/
function format(numArr) {
const cIntl = new Intl.NumberFormat('en-GB', {
notation: 'compact',
compactDisplay: 'short',
});
return customFunctionRecurse_(numArr, format, (num) => cIntl.format(num));
}
But for extreme ends or custom formatting, We need to use a custom script:
/**
* Formats various numbers according to the provided format
* #customfunction
* #author TheMaster https://stackoverflow.com/users/8404453
* #param {A1:D2} numArr A 2D array
* #param {X1:Y2} formatArr [optional] A format 2D real/virtual array
* with base 10 power -> suffix mapping
* eg: X1:3 Y1:K represents numbers > 10^3 should have a K suffix
* #param {3} suffixPadLength [optional] Fix suffix to fixed length by padding spaces
* #returns {String[][]} Formatted 2D array
*/
function customFormat(
numArr,
formatArr = [
/**This formatArr array is provided by
* by player0 https://stackoverflow.com/users/5632629/
* #see https://stackoverflow.com/questions/69773823#comment123503634_69809210
*/
[3, 'K'], //Thousand
[6, 'M'], //Million
[9, 'B'], //Billion
[12, 'T'], //Trillion
[15, 'Qa'], //Quadrillion
[18, 'Qi'], //Quintillion
[21, 'Sx'], //Sextillion
[24, 'Sp'], //Septillion
[27, 'O'], //Octillion
[30, 'N'], //Nonillion
[33, 'D'], //Decillion
[36, 'Ud'], //Undecillion
[39, 'Dd'], //Duodecillion
[42, 'Td'], //Tredecillion
[45, 'Qad'], //Quattuordecillion
[48, 'Qid'], //Quindecillion
[51, 'Sxd'], //Sexdecillion
[54, 'Spd'], //Septendecillion
[57, 'Od'], //Octodecillion
[60, 'Nd'], //Novemdecillion
[63, 'V'], //Vigintillion
[66, 'Uv'], //Unvigintillion
[69, 'Dv'], //Duovigintillion
[72, 'Tv'], //Trevigintillion
[75, 'Qav'], //Quattuorvigintillion
[78, 'Qiv'], //Quinvigintillion
[81, 'Sxv'], //Sexvigintillion
[84, 'Spv'], //Septenvigintillion
[87, 'Ov'], //Octovigintillion
[90, 'Nv'], //Novemvigintillion
[93, 'Tr'], //Trigintillion
[96, 'Ut'], //Untrigintillion
[99, 'Dt'], //Duotrigintillion
[102, 'G'], //Googol
],
suffixPadLength = 3,
inRecursion = false
) {
if (!inRecursion) {
formatArr = formatArr.reverse();
formatArr.push([0, '']);
}
const addSuffix = num => {
const pad3 = (str = '') => str.padEnd(suffixPadLength, ' '); //pad 3 spaces if necessary
const anum = Math.abs(num);
if (num === 0) return '0' + pad3();
if (anum > 0 && anum < 1) return String(num.toFixed(2)) + pad3();
for (const [exp, suffix] of formatArr) {
if (anum >= Math.pow(10, exp))
return `${(num / Math.pow(10, exp)).toFixed(2)}${pad3(suffix)}`;
}
};
return customFunctionRecurse_(
numArr,
customFormat,
addSuffix,
formatArr,
suffixPadLength,
true
);
}
Usage:
=CUSTOMFORMAT(A1:A5,{{3,"k"};{10,"G"}})
Tells custom function to use k for numbers>10^3 and G for 10^10
Illustration:
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
/**
* Utility function needed to map 2D arrays
*/
function customFunctionRecurse_(array, mainFunc, subFunc, extraArgToMainFunc) {
if (Array.isArray(array))
return array.map((e) => mainFunc(e, extraArgToMainFunc));
else return subFunc(array);
}
/**
* Simple custom formating function using Intl
* #see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
* #customfunction
* #param {A1:D2} A 2D array
* #returns {String[][]}Compact Intl formatted 2D array
*/
function format(numArr) {
const cIntl = new Intl.NumberFormat('en-GB', {
notation: 'compact',
compactDisplay: 'short',
});
return customFunctionRecurse_(numArr, format, (num) => cIntl.format(num));
}
/**
* Formats various numbers according to the provided format
* #customfunction
* #param {A1:D2} A 2D array
* #param {X1:Y2=} [optional] A format 2D real/virtual array
* with base 10 power -> suffix mapping
* eg: X1:3 Y1:K represents numbers > 10^3 should have a K suffix
* #returns {String[][]} Formatted 2D array
*/
function customFormat(
numArr,
formatArr = [
//sample byte => kb formatting
[3, 'kb'],
[6, 'mb'],
[9, 'gb'],
[12, 'tb'],
]
) {
//console.log({ numArr, formatArr });
if (
formatArr[formatArr.length - 1] &&
formatArr[formatArr.length - 1][0] !== 0
) {
formatArr = formatArr.reverse();
formatArr.push([0, '']);
}
const addSuffix = (num) => {
const anum = Math.abs(num);
if (num === 0) return '0.00';
if (anum > 0 && anum < 1) return String(num.toFixed(2));
for (const [exp, suffix] of formatArr) {
if (anum >= Math.pow(10, exp))
return `${(num / Math.pow(10, exp)).toFixed(2)}${suffix}`;
}
};
return customFunctionRecurse_(numArr, customFormat, addSuffix, formatArr);
}
console.log(
customFormat([
[
0,
1000,
153,
12883255,
235688235123,
88555552233355888,
-86555,
0.8523588055,
Math.pow(10, 15),
],
])
);
<!-- https://meta.stackoverflow.com/a/375985/ --> <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

sometimes when we deal with nuclear physics we need to shorten the time so this is how:
=INDEX(IF(ISBLANK(A2:A),,TEXT(TRUNC(TEXT(IF(A2:A*1<1,
TEXT(A2:A*1, "0."&REPT(0, 30))*VLOOKUP(A2:A*1, {SORT({0; 1/10^SEQUENCE(9, 1, 3, 3)}),
{0; 10^SORT(SEQUENCE(9, 1, 3, 3), 1,)}}, 2, 1), TEXT(A2:A*1, REPT(0, 30))/
VLOOKUP(A2:A*1, TEXT({1; 60; 3600; 86400; 31536000; 31536000*10^SEQUENCE(8, 1, 3, 3)},
{"#", "#"})*1, 2, 1)), "0."&REPT("0", 30)), 3), "0.000")&" "&
VLOOKUP(A2:A*1, {SORT({0; 1/10^SEQUENCE(9, 1, 3, 3);
{1; 60; 3600; 86400; 31536000}; 31536000*10^SEQUENCE(8, 1, 3, 3)}), FLATTEN(SPLIT(
"s ys zs as fs ps ns μs ms s m h d y ky My Gy Ty Py Ey Zy Yy", " ",,))}, 2, 1)))
it's a simple conversion from seconds into abbreviation utilizing the International System of Units where:
in seconds
____________________________________
ys = yoctosecond = 0.000000000000000000000001
zs = zeptosecond = 0.000000000000000000001
as = attosecond = 0.000000000000000001
fs = femtosecond = 0.000000000000001
ps = pikosecond = 0.000000000001
ns = nanosecond = 0.000000001
μs = microsecond = 0.000001
ms = millisecond = 0.001
s = second = 1
m = minute = 60
h = hour = 3600
d = day = 86400
y = year = 31536000
ky = kiloyear = 31536000000
My = megayear = 31536000000000
Gy = gigayear = 31536000000000000
Ty = terayear = 31536000000000000000
Py = petayear = 31536000000000000000000
Ey = exayear = 31536000000000000000000000
Zy = zettayear = 31536000000000000000000000000
Yy = yottayear = 31536000000000000000000000000000

Related

How to use an array as index for decision variable in cplex

I am new in cplex. I want to use the two arrays that I have in my code as indexes for my decision variables and parameters. I need the values ​​of the arrays as indices, not the sizes of the arrays, too. I saw these links: "https://github.com/AlexFleischerParis/zooopl/blob/master/zooarrayvariableindexerunion.mod" and other examples in https://stackoverflow.com for my problem but I don't know How should I change my code?
.mod
{int} V = {11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53};
range F=1..20;
int sizeFlow[f in F]=2+rand(3);
int n=card(V);
int m=4;
range r=1..n;
// scripting way that will get m times 1
range subr=1..m;
int t[f in F][i in subr]=1+rand(n+1-i);
{int} setn[f in F]=asSet(r);
int x2[F][i in r];
execute
{
for (var f in F) for(var i in subr)
{
var e=t[f][i];
var e2=Opl.item(setn[f],e-1);
x2[f][e2]=1;
setn[f].remove(e2);
}
}
{int} result[f in F]={i | i in r:x2[f][i]==1};
{int} Flow4[f in F]=union (i in result[f]) {item(V,i-1)};
{int} Flow[f in F]={item(Flow4[f],j-1) | j in 1..sizeFlow[f]};
assert forall(f in F) forall(ordered i,j in Flow[f]) i!=j;
execute{ writeln(Flow); }
Now I want to use arrays sizeFlow[f] and Flow[f] in rest of my code, For example:
range Nunode1 = 0..10;
tuple edge{
key int origin;
key int destination;}
{edge} Edges with origin,destination in Nunode1 = {<0,1>,<1,3>,<2,3>,<3,4>,<3,5>,<3,6>,
<4,5>,<4,6>,<4,8>,<5,6>,<6,7>,<6,9>,<9,10>};
//transmission rate.
float C[Edges]=...;
float landa[Flow[f]] = 0.5 +rand(2);
//DECISION VARIABLES
dvar boolean IL[Edges][Flow[f]][sizeFlow[f]][Nunode1][sizeFlow[f]][Nunode1]; //denotes
that link l is used by flow f to route from the j-th to (j + 1)-th NF service, hosted at
node nj and nj+1.
//Objective function
dexpr float objmodel2 = sum(l in Edges, c in 1..Flow[f], j in 1..sizeFlow[f]: (j+1) in
1..sizeFlow[f] && (j+1)>j, n in Nunode1: (n+1) in Nunode1 && (n+1)>n) ((IL[l][c][j][n]
[j+1][n+1] * landa[c]) / C[l]); //to minimize the utilization of link capacities.
minimize objmodel2;
subject to{
forall (l in Edges)
cons1: sum(c in 1..Flow[f], j in 1..sizeFlow[f]: j+1 in 1..sizeFlow[f], n in Nunode1:
(n+1) in Nunode1) (IL[l][c][j][n][j+1][n+1] * landa[c]) <= C[l];}

Parse int and float values from Uint8List Dart

I'm trying to parse int and double values which I receive from a bluetooth device using this lib: https://github.com/Polidea/FlutterBleLib
I receive the following Uint8List data: 31,212,243,57,0,224,7,1,6,5,9,21,0,1,0,0,0,91,228
I found some help here: How do I read a 16-bit int from a Uint8List in Dart?
On Android I have done some similar work, but the library there had so called Value Interpreter which I only passed the data and received back float/int.
Example code from Android:
int offset = 0;
final double spOPercentage = ValueInterpreter.getFloatValue(value, FORMAT_SFLOAT, offset);
Where value is a byte array
Another example from android code, this code if from the library:
public static Float getFloatValue(#NonNull byte[] value, int formatType, #IntRange(from = 0L) int offset) {
if (offset + getTypeLen(formatType) > value.length) {
return null;
} else {
switch(formatType) {
case 50:
return bytesToFloat(value[offset], value[offset + 1]);
case 52:
return bytesToFloat(value[offset], value[offset + 1], value[offset + 2], value[offset + 3]);
default:
return null;
}
}
}
private static float bytesToFloat(byte b0, byte b1) {
int mantissa = unsignedToSigned(unsignedByteToInt(b0) + ((unsignedByteToInt(b1) & 15) << 8), 12);
int exponent = unsignedToSigned(unsignedByteToInt(b1) >> 4, 4);
return (float)((double)mantissa * Math.pow(10.0D, (double)exponent));
}
private static float bytesToFloat(byte b0, byte b1, byte b2, byte b3) {
int mantissa = unsignedToSigned(unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8) +
(unsignedByteToInt(b2) << 16), 24);
return (float)((double)mantissa * Math.pow(10.0D, (double)b3));
}
private static int unsignedByteToInt(byte b) {
return b & 255;
}
In flutter/dart I want to write my own value interpreter.
The starting example code is:
int offset = 1;
ByteData bytes = list.buffer.asByteData();
bytes.getUint16(offset);
I don't understand how data is manipulated here in dart to get a int value from different position from data list. I need some explanation how to do this, would be great if anyone can give some teaching about this.
Having the following:
values [31, 212, 243, 57, 0, 224, 7, 1, 6, 5, 9, 21, 0, 1, 0, 0, 0, 91, 228];
index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
When you make:
values.list.buffer.asByteData().getUint16(0);
you interpret [31, 212] as a single unsigned int of two bytes length.
If you want to get a Uint16 from bytes 9 and 10 [5, 9], you'd call:
values.list.buffer.asByteData().getUint16(9);
Regarding your comment (Parse int and float values from Uint8List Dart):
I have this Uint8List and the values are: 31, 212, 243, 57, 0, 224, 7, 1, 6, 5, 9, 21, 0, 1, 0, 0, 0, 91, 228 I use the code below ByteData bytes = list.buffer.asByteData(); int offset = 1; double value = bytes.getFloat32(offset); and value that I expected should be something between 50 and 150 More info on what I am doing can be found here: bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/… name="SpO2PR-Spot-Check - SpO2"
This property is of type SFLOAT, which according to https://www.bluetooth.com/specifications/assigned-numbers/format-types/ looks like this:
0x16 SFLOAT IEEE-11073 16-bit SFLOAT
As Dart does not seem to have an easy way to get that format, you might have to create a parser yourself using raw bytes.
These might be helpful:
https://stackoverflow.com/a/51391743/6413439
https://stackoverflow.com/a/16474957/6413439
Here is something that I used to convert sfloat to double in dart for our flutter app.
double sfloat2double(ieee11073) {
var reservedValues = {
0x07FE: 'PositiveInfinity',
0x07FF: 'NaN',
0x0800: 'NaN',
0x0801: 'NaN',
0x0802: 'NegativeInfinity'
};
var mantissa = ieee11073 & 0x0FFF;
if (reservedValues.containsKey(mantissa)){
return 0.0; // basically error
}
if ((ieee11073 & 0x0800) != 0){
mantissa = -((ieee11073 & 0x0FFF) + 1 );
}else{
mantissa = (ieee11073 & 0x0FFF);
}
var exponent = ieee11073 >> 12;
if (((ieee11073 >> 12) & 0x8) != 0){
exponent = -((~(ieee11073 >> 12) & 0x0F) + 1 );
}else{
exponent = ((ieee11073 >> 12) & 0x0F);
}
var magnitude = pow(10, exponent);
return (mantissa * magnitude);
}

How to divide Time in float form (hh.mm) by an integer in ruby?

I am trying to divide Time in float form (hh.mm) by an integer.
For example 1.30 by 2 must give 00.45.
Is there any simple way to do this?
Using a float to express h.mm is a bit unusual. You would typically use strings for formatting.
However, I'd start by extracting hours and minutes from the float value. To do so, I would convert the float to a string using format:
time = 1.3
time_str = format('%.2f', time)
#=> "1.30"
Then I would split the string at . to get the hour part and minutes part and call to_i to convert them to actual integers: (I'm using map here, you could also call h = h.to_i / m = m.to_i afterwards)
h, m = time_str.split('.').map(&:to_i)
h #=> 1
m #=> 30
Now that we have the numbers 1 and 30 as integers, we can easily calculate the total duration in minutes:
duration = h * 60 + m
#=> 90
I would then divide the duration by 2 (or whatever value):
duration /= 2
#=> 45
and convert it back to hours and minutes using divmod: (it returns both values at once)
h, m = duration.divmod(60)
h #=> 0
m #=> 45
We can format these as a string:
format('%02d.%02d', h, m)
#=> "00.45"
or convert it back to a float:
time = h + m.fdiv(100)
#=> 0.45
Which can be formatted like this:
format('%05.2f', time)
#=> "00.45"
time = 1.3
divisor = 2
hr, min = (time.fdiv(divisor)).divmod(1)
#=> [0, 0.65]
min = (60 * min).round
#=> 39
"%02d.%02d" % [hr, min]
#=> "00.39"
Another example.
time = 1005
divisor = 5
hr, min = (time.fdiv(divisor)).divmod(1)
#=> [201, 0.0]
"%02d.%02d" % [hr, (60 * min).round]
#=> "201.00"
See Integer#fdiv, Float#fdiv, Integer#divmod and Integer#round. divmod is an extremely useful method that, for reasons I don't understand, seems to be under-used.
Maybe you can split into an array an then:
n = 2
[1, 30].then { |h, m| [h / n, (m + h % n * 60) / n]}
#=> [0, 45]
For splitting:
num = 1.3
('%.2f' % num).split('.').map(&:to_i) #=> [1, 30]
You can try the following :
num = 1.3
splitted_values = ('%.2f' % num).split('.').map(&:to_i) => [1, 30]
((splitted_values[0] * 60) / 2) + (splitted_values[1] / 2 ) => 45

Creating a range from one column

I have a column called "Marks" which contains values like
Marks = [100,200,150,157,....]
I need to assign Grades to those marks using the following key
<25=0, <75=1, <125=2, <250=3, <500=4, >500=5
If Marks < 25, then Grade = 0, if marks < 75 then grade = 1.
I can sort the results and find the first record that matches using Ruby's find function. Is it the best method ? Or is there a way by which I can prepare a range using the key by adding Lower Limit and Upper Limit columns to the table and by populating those ranges using the key? Marks can have decimals too Ex: 99.99
Without using Rails, you could do it like this:
marks = [100, 200, 150, 157, 692, 12]
marks_to_grade = { 25=>0, 75=>1, 125=>2, 250=>3, 500=>4, Float::INFINITY=>5 }
Hash[marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
With Ruby 2.1, you could write this:
marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }.to_h
Here's what's happening:
Enumerable#map (a.k.a collect) converts each mark m to an array [m, g], where g is the grade computed for that mark. For example, when map passes the first element of marks into its block, we have:
m = 100
a = marks_to_grade.find { |k,_| m <= k }
#=> marks_to_grade.find { |k,_| 100 <= k }
#=> [125, 2]
a.last
#=> 2
so the mark 100 is mapped to [100, 2]. (I've replaced the block variable for the value of the key-value pair with the placeholder _ to draw attention to the fact that the value is not being used in the calculation within the block. One could also use, say, _v as the placeholder.) The remaining marks are similarly mapped, resulting in:
b = marks.map { |m| [m, marks_to_grade.find { |k,_| m <= k }.last] }
#=> [[100, 2], [200, 3], [150, 3], [157, 3], [692, 5], [12, 0]]
Lastly
Hash[b]
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
or, for Ruby 2.1+
b.to_h
#=> {100=>2, 200=>3, 150=>3, 157=>3, 692=>5, 12=>0}
You can make use of update_all:
Student.where(:mark => 0...25).update_all(grade: 0)
Student.where(:mark => 25...75).update_all(grade: 1)
Student.where(:mark => 75...125).update_all(grade: 2)
Student.where(:mark => 125...250).update_all(grade: 3)
Student.where(:mark => 250...500).update_all(grade: 4)
Student.where("mark > ?", 500).update_all(grade: 5)

Hashfunction to map combinations of 5 to 7 cards

Referring to the original problem: Optimizing hand-evaluation algorithm for Poker-Monte-Carlo-Simulation
I have a list of 5 to 7 cards and want to store their value in a hashtable, which should be an array of 32-bit-integers and directly accessed by the hashfunctions value as index.
Regarding the large amount of possible combinations in a 52-card-deck, I don't want to waste too much memory.
Numbers:
7-card-combinations: 133784560
6-card-combinations: 20358520
5-card-combinations: 2598960
Total: 156.742.040 possible combinations
Storing 157 million 32-bit-integer values costs about 580MB. So I would like to avoid increasing this number by reserving memory in an array for values that aren't needed.
So the question is: How could a hashfunction look like, that maps each possible, non duplicated combination of cards to a consecutive value between 0 and 156.742.040 or at least comes close to it?
Paul Senzee has a great post on this for 7 cards (deleted link as it is broken and now points to a NSFW site).
His code is basically a bunch of pre-computed tables and then one function to look up the array index for a given 7-card hand (represented as a 64-bit number with the lowest 52 bits signifying cards):
inline unsigned index52c7(unsigned __int64 x)
{
const unsigned short *a = (const unsigned short *)&x;
unsigned A = a[3], B = a[2], C = a[1], D = a[0],
bcA = _bitcount[A], bcB = _bitcount[B], bcC = _bitcount[C], bcD = _bitcount[D],
mulA = _choose48x[7 - bcA], mulB = _choose32x[7 - (bcA + bcB)], mulC = _choose16x[bcD];
return _offsets52c[bcA] + _table4[A] * mulA +
_offsets48c[ (bcA << 4) + bcB] + _table [B] * mulB +
_offsets32c[((bcA + bcB) << 4) + bcC] + _table [C] * mulC +
_table [D];
}
In short, it's a bunch of lookups and bitwise operations powered by pre-computed lookup tables based on perfect hashing.
If you go back and look at this website, you can get the perfect hash code that Senzee used to create the 7-card hash and repeat the process for 5- and 6-card tables (essentially creating a new index52c7.h for each). You might be able to smash all 3 into one table, but I haven't tried that.
All told that should be ~628 MB (4 bytes * 157 M entries). Or, if you want to split it up, you can map it to 16-bit numbers (since I believe most poker hand evaluators only need 7,462 unique hand scores) and then have a separate map from those 7,462 hand scores to whatever hand categories you want. That would be 314 MB.
Here's a different answer based on the colex function concept. It works with bitsets that are sorted in descending order. Here's a Python implementation (both recursive so you can see the logic and iterative). The main concept is that, given a bitset, you can always calculate how many bitsets there are with the same number of set bits but less than (in either the lexicographical or mathematical sense) your given bitset. I got the idea from this paper on hand isomorphisms.
from math import factorial
def n_choose_k(n, k):
return 0 if n < k else factorial(n) // (factorial(k) * factorial(n - k))
def indexset_recursive(bitset, lowest_bit=0):
"""Return number of bitsets with same number of set bits but less than
given bitset.
Args:
bitset (sequence) - Sequence of set bits in descending order.
lowest_bit (int) - Name of the lowest bit. Default = 0.
>>> indexset_recursive([51, 50, 49, 48, 47, 46, 45])
133784559
>>> indexset_recursive([52, 51, 50, 49, 48, 47, 46], lowest_bit=1)
133784559
>>> indexset_recursive([6, 5, 4, 3, 2, 1, 0])
0
>>> indexset_recursive([7, 6, 5, 4, 3, 2, 1], lowest_bit=1)
0
"""
m = len(bitset)
first = bitset[0] - lowest_bit
if m == 1:
return first
else:
t = n_choose_k(first, m)
return t + indexset_recursive(bitset[1:], lowest_bit)
def indexset(bitset, lowest_bit=0):
"""Return number of bitsets with same number of set bits but less than
given bitset.
Args:
bitset (sequence) - Sequence of set bits in descending order.
lowest_bit (int) - Name of the lowest bit. Default = 0.
>>> indexset([51, 50, 49, 48, 47, 46, 45])
133784559
>>> indexset([52, 51, 50, 49, 48, 47, 46], lowest_bit=1)
133784559
>>> indexset([6, 5, 4, 3, 2, 1, 0])
0
>>> indexset([7, 6, 5, 4, 3, 2, 1], lowest_bit=1)
0
"""
m = len(bitset)
g = enumerate(bitset)
return sum(n_choose_k(bit - lowest_bit, m - i) for i, bit in g)

Resources