9
\$\begingroup\$

Your function must accept 2 strings which are correct numbers. It needs to sum them up (without rounding and floating point errors) and return the result (which is also a correct number) without any leading zeros in the integer part, trailing zeros in the decimal part and without minus if the answer is 0 i.e. -0 is not allowed.

A correct number is a string which can start with a leading minus, contains only digits and can contain only one dot which will be between the digits:

'1234567890'
'-1234567890'
'12345.67890'
'-12345.67890'

In other words, it matches the regex -?\d+(\.\d+)?

Your input will always be valid so you don't need to think about empty strings, etc.

The input strings can be as large as possible in your programming language, but it is guaranteed that the length of the resulting string will not be larger than what is available in your programming language.

Some test cases:

'1', '1' --> '2'
'0', '0' --> '0'
'9', '1' --> '10'
'001.002', '3.400' --> '4.402'
'5', '-5.0' --> '0'
'2', '-2.5' --> '-0.5'
'12345', '67890' --> '80235'
'-50.6', '20.53' --> '-30.07'
'-001.00', '00100' --> '99'
'-00.100', '-0.20' --> '-0.3'
'0.1', '0.2' --> '0.3'
'1000000000000', '-0.000000000001' --> '999999999999.999999999999'

The shortest code in each programming language wins!

\$\endgroup\$
12
  • 3
    \$\begingroup\$ How I must understand what is not clear for you? It is better to write a comment than just voting to close and I will add additional information \$\endgroup\$
    – EzioMercer
    Feb 6 at 9:15
  • 2
    \$\begingroup\$ @JosWoolley If in Excel you can do it simply than it is not a problem :) A lot of programming languages can't work properly with any decimal number, so if you don't have any issues with this problem in your language then it is great and you can write an answer with test cases \$\endgroup\$
    – EzioMercer
    Feb 6 at 9:28
  • 6
    \$\begingroup\$ I don't see how this question Needs Details or Clarity. It's probably been done before, so if anyone who wants this closed can go through the effort to find a dupe target, fair game, but it seems perfectly clear what the task is. \$\endgroup\$ Feb 6 at 14:17
  • 3
    \$\begingroup\$ @EzioMercer most regex syntax is identical across languages. the one I added would be exactly the same in absolutely any language (assuming the backslashes are properly escaped) \$\endgroup\$
    – Seggan
    Feb 6 at 17:48
  • 8
    \$\begingroup\$ The issue with this challenge is that it is essentially a chameleon challenge. While the title says that the goal is to "Sum numbers which are strings", the real challenge is to do so without any precision issues from floats or any rounding issues, leading to a lot of invalid answers only looking at the first part \$\endgroup\$
    – Jo King
    Feb 8 at 1:37

15 Answers 15

8
\$\begingroup\$

Raku, 17 15 bytes

$~*+*o**.FatRat

Try it online!

This converts both arguments to FatRats (arbitrary sized bignums), before adding them together and stringifying the result. You could remove the o**.FatRat part, but this would use Rats, which decay to floating point numbers once the precision needed is too high.

\$\endgroup\$
2
  • \$\begingroup\$ With use 6.e.PREVIEW; one can write $*RAT-OVERFLOW = FatRat; to obviate the need for the o**.FatRat part in the dynamic scope of such a statement. (Maybe it would be worth discussing the pros and cons of switching to $*RAT-OVERFLOW = FatRat as the default for 6.e?) Also, I don't know if there's a convenient way to add such a statement to a SETTING for execution of the raku binary but that would make it particularly nice. (If there isn't, maybe it would be worth discussing that too.) \$\endgroup\$
    – raiph
    Feb 16 at 17:22
  • \$\begingroup\$ It took me a while to understand the Footer code in your tio. For the record I wrote this alternative: { say "'$0', '$1' --> '$2'" => f $0, $1 with $/ = .comb: / \' <(.*?)> \' / } for lines. \$\endgroup\$
    – raiph
    Feb 16 at 17:24
7
\$\begingroup\$

Java 8, 103 102 bytes

import java.math.*;a->b->new BigDecimal(a).add(new BigDecimal(b)).stripTrailingZeros().toPlainString()

Try it online.

Explanation:

import java.math.*;        // Required import for BigDecimal
a->b->                     // Method with String as two parameters & return-type
  new BigDecimal(a)        //  Convert the first input to a BigDecimal
   .add(                   //  Plus:
        new BigDecimal(b)) //   The second input as BigDecimal
  .stripTrailingZeros()    //  Remove any no-op trailing 0s
  .toPlainString()         //  Convert and return it as plain String (so no scientific
                           //  notation)
\$\endgroup\$
5
  • \$\begingroup\$ Those builtin names are so long though... \$\endgroup\$
    – Neil
    Feb 6 at 12:47
  • \$\begingroup\$ @Neil ikr.. Java is verbose as fwck.. :/ I've looked in the source code of both those methods to see what they do, but unfortunately it can't really be shortened manually. And java.math.BigDecimal is verbose to begin with.. \$\endgroup\$ Feb 6 at 13:07
  • \$\begingroup\$ I don't know Java, so just out of interest: can't you save a few bytes if you store new java.math.BigDecimal in a variable like d and then just call d(a) and/or ` d(b)`? \$\endgroup\$
    – EzioMercer
    Feb 6 at 14:39
  • 2
    \$\begingroup\$ @EzioMercer Unfortunately no. Java can't really save methods in variables, unless they're lambdas. In addition, new java.math.BigDecimal(String) is a constructor instead of a method. There also isn't a java.math.BigDecimal.valueOf(String) method; valueOf only accepts numeric argument-types. Although even if there was such a method, the lambda would require a body with return, so a->b->{java.math.BigDecimal T=null;return T.valueOf(a).add(T.valueOf(b)).stripTrailingZeros().toPlainString();} would still be 8 bytes longer. \$\endgroup\$ Feb 6 at 14:48
  • \$\begingroup\$ Thank you for detailed explanation! \$\endgroup\$
    – EzioMercer
    Feb 6 at 14:52
6
\$\begingroup\$

Fig, \$5\log_{256}(96)\approx\$ 4.116 bytes

B+_x_

Try it online!

Since Fig uses BigDecimals, this doesn't suffer from floating point issues as some other answers do.

B+_x_
    _ # Convert first input to number
  _x  # Convert second input to number
 +    # Add
B     # Convert to string
\$\endgroup\$
4
  • \$\begingroup\$ You're expected to remove trailing zeros after the ., even when those were included in the inputs. \$\endgroup\$
    – Neil
    Feb 7 at 10:23
  • \$\begingroup\$ @Neil does it not? \$\endgroup\$
    – Seggan
    Feb 7 at 14:43
  • \$\begingroup\$ If I put in "0.44" and "0.56" as inputs then the displayed result is 1.00 but I think it's only supposed to display 1. And then there are some of the test cases such as '-001.00', '00100' --> '99'. \$\endgroup\$
    – Neil
    Feb 7 at 17:21
  • \$\begingroup\$ Hrm that would be a bug in Fig. Printing should always remove trailing zeros \$\endgroup\$
    – Seggan
    Feb 7 at 18:51
4
\$\begingroup\$

Python, 84 bytes

lambda*x:(s:=str(sum(map(Decimal,x))))[-2:]=='.0'and s[:-2]or s
from decimal import*

Attempt This Online!

Normal numbers succumb to floating point inaccuracies, so we need to convert to decimal.Decimal.

\$\endgroup\$
1
  • \$\begingroup\$ There does seem to be an issue of the output size being limited, e.g add a few more zeroes to the 0.00..001 case and it will round it up. I don't know how one would fix this \$\endgroup\$
    – Jo King
    Feb 7 at 21:36
3
\$\begingroup\$

bash, + bc, 30 bytes

bc<<<$1+$2|sed -r "s/\.*0+$//"

Try it online!

How?

This takes advantage of bc being an arbitrary precision calculator language. It pipes the input (expected to be two arguments) to bc which adds them.

10 bytes to do the calculation, bc<<<$1+$2. 20 bytes to pipe to sed and remove trailing zeroes.

The golf-y bit is replacing echo with the bash here string (<<<), as in this tip. The code is the equivalent of echo $1+$2 | bc. This is my first attempt at a challenge in a language other than R. I would be interested in tips to golf further.

Try it online!

Example output

I am not exactly sure how to provide a list of string test cases on TIO. In the absence of that, here is the example output for the final test case on my machine (script is called calc.sh):

$ ./calc.sh '1000000000000' '-0.000000000001'
999999999999.999999999999
$ ./calc.sh '-00.100' '-0.20'
-.3

bash + bc, 31 bytes

This did not strip all trailing zeroes, as pointed out in the comments by @roblogic.

m=`bc<<<$1+$2`
echo ${m//[0$]/}

Try it online!

Answer which did not meet spec, 10 bytes

bc<<<$1+$2

Unfortunately, as pointed out by
Ismael Miguel in the comments, the question says to

return the result (which is also a correct number) without any leading zeros in the integer part, trailing zeros in the decimal part and without minus if the answer is 0

This did not remove all trailing zeroes.

\$\endgroup\$
5
  • \$\begingroup\$ This fails for the test case '-00.100', '-0.20' --> '-0.3' - it returns -.300 instead of -0.3. But confirm with O. P. if this is fine or not. \$\endgroup\$ Feb 8 at 20:09
  • \$\begingroup\$ @IsmaelMiguel good point - I have updated. Makes it a lot longer unfortunately. It now reurns '.3', i.e. no trailing zeroes, and no leading zeroes in the integer part, which is what the challenge says (although not what the example output shows...) \$\endgroup\$
    – SamR
    Feb 8 at 20:46
  • 1
    \$\begingroup\$ I'm sorry it bumped the size quite a fair bit... But I've upvoted your answer. \$\endgroup\$ Feb 8 at 20:49
  • 1
    \$\begingroup\$ This gives incorrect results like 99., -.3, .3 for a few of the test cases. Maybe try sed to remove trailing . / add leading 0 or -0 for those cases. \$\endgroup\$
    – roblogic
    Feb 9 at 14:41
  • \$\begingroup\$ @roblogic thanks that was helpful - have updated. It still does not add a leading zero, as the spec says without any leading zeros in the integer part, trailing zeros in the decimal part and without minus if the answer is 0. I don't think that means there needs to be one. \$\endgroup\$
    – SamR
    Feb 9 at 15:15
3
\$\begingroup\$

Charcoal, 72 69 bytes

≔E²Sθ≔Eθ∧№ι.⌕⮌ι.η≔ΣIEθ⁺⁻ι.×0⁻⌈η§ηκθ≔Xχ⌈ηη‹θ⁰I÷↔θη¿﹪θη«.W﹪↔θη«≧÷χηI÷ιη

Try it online! Link is to verbose version of code. Feels very long somehow. Explanation:

≔E²Sθ

Input the two strings.

≔Eθ∧№ι.⌕⮌ι.η

See how many digits each has after the decimal (if any).

≔ΣIEθ⁺⁻ι.×0⁻⌈η§ηκθ

Scale them both to be integers and take the sum.

≔Xχ⌈ηη‹θ⁰I÷↔θη

Calculate the effect of the scaling and output the integer part of the result.

¿﹪θη«.

If the result is not an integer then output a ..

W﹪↔θη«≧÷χηI÷ιη

Output successive digits until the remainder is zero.

40 bytes by importing Python's decimal.Decimal:

≔IΣE²▷⪫⟦d¦.Dω⟧ecimalSθW›№θ.Σ§θ±¹≔…θ⊖Lθθθ

Attempt This Online! Link is to verbose version of code. Explanation:

≔IΣE²▷⪫⟦d¦.Dω⟧ecimalSθ

Input the two strings, convert them to decimal.Decimal, take the sum, then cast back to string again.

W›№θ.Σ§θ±¹

Repeat while the string contains a . and does not end in a digit 1-9...

≔…θ⊖Lθθ

... remove the last character.

θ

Output the final string.

\$\endgroup\$
2
\$\begingroup\$

PARI/GP, 163 bytes

g(s)=eval(strjoin(t=strsplit(s,".")))/10^if(#t>1,#t[2])
h(n)=if(n*=10,Str(n\1,h(n-n\1)),"")
f(a,b)=Str(if(0>t=g(a)+g(b),t=-t;"-","")d=t\1,if(t-=d,Str("."h(t)),""))

Attempt This Online!

\$\endgroup\$
2
\$\begingroup\$

Japt, 1, 3 or 19 bytes

None of these can handle the last test case as the decimal exceeds JavaScript's native number support. Links include all other test cases, taking input as an array of 2 strings.

Outputs an integer, subject to floating point inaccuracies.

x

Try it

Outputs an string, subject to floating point inaccuracies.

x s

Try it

Outputs a string, with no floating point inaccuracies.

x x¡X°s q. hP ÌÊÃrÔ

Try it

\$\endgroup\$
5
  • 5
    \$\begingroup\$ ['00.10', '0.200'] --> '0.30000000000000004' - it is not correct and if I'm correct the output is number not a string \$\endgroup\$
    – EzioMercer
    Feb 6 at 9:46
  • 5
    \$\begingroup\$ Not OP but I think the many 0s are the result of floating point addition error which occurs frequently \$\endgroup\$ Feb 6 at 12:09
  • \$\begingroup\$ Undone is correct, @EzioMercer; that's due to floating point inaccuracies in JavaScript. \$\endgroup\$
    – Shaggy
    Feb 7 at 11:41
  • \$\begingroup\$ I know why this happens and therefore I gave a lot of test cases for you to check your function for operability :) \$\endgroup\$
    – EzioMercer
    Feb 7 at 12:17
  • 1
    \$\begingroup\$ @EzioMercer, see the very delibrerate & obvious note preceding my solutions. \$\endgroup\$
    – Shaggy
    Feb 7 at 13:09
2
\$\begingroup\$

Excel, 7 9 bytes

=A1+B1&""
\$\endgroup\$
7
  • \$\begingroup\$ Are the inputs considered as strings in Excel? does it work with cells formatted as text? \$\endgroup\$
    – Kaddath
    Feb 7 at 14:21
  • 1
    \$\begingroup\$ @Kaddath As per the specifications, the inputs are expected to be strings. And yes, it works with cells formatted as text, since that would imply a string. \$\endgroup\$ Feb 7 at 14:24
  • \$\begingroup\$ If you require the inputs to be x and y, how are those input? Are you required to setup named ranges with those names and then set their value? That is beyond what's normally included for Excel answers as opposed to =A1+B1&"", which is still a good answer. \$\endgroup\$ Feb 7 at 15:57
  • \$\begingroup\$ this should work on google sheets too \$\endgroup\$ Feb 7 at 17:52
  • 1
    \$\begingroup\$ Fair point. If it was another language, whatever bytes were required format args as strings wouldn't be in the byte count. In Excel, you either have to format the cells as text (extra clicks or keyboard input not in the byte count) or input the values into other cells as text ('1 which doesn't count directly but requires a cell reference instead of just x in the function) or changing the formula to add your input as explicit strings (="1"+"2"&"" which also raises the byte count). This may be a question for meta but there's only a handful of Excel golfers so may not be worth it. \$\endgroup\$ Feb 8 at 13:25
2
\$\begingroup\$

PHP 8.x + BC Math, 46 bytes

It's a bit longer than expected, but does the job.

fn($a,$b)=>rtrim(rtrim(bcadd($b,$a,99),0),'.')

This creates an anonymous function that returns the expected result.


How does it work?

It uses the bcadd function to do all the math, using an obnoxiously high scale, just to be safe.

That function accepts 2 strings and returns a string with the calculations made.

After that, removes all 0's from the right, then the period (.).
If none is found, does nothing to the result.


You can try it on here - with testcases:
https://onlinephp.io?s=VVHNSsNAED4byDvMIbAJbLabpH9RY_EgeBDswVsIkiYbItSmJNsiiG8gePGo7-Hz-AK-gpPduK17mJn9vvnmhzlfbOutbdnWaASX6zVI0Uko8k50tuU8QQKpbZ2kJCAUtAlJRhXE-99gBig2WYHBOA8Y52EPRmzMlWCMgakz6RF_wv6XUgI_ZJrl6AciCKOxAqezeaw0cx5GhvYnnE3VnKiJlDjijM8Mr-dRvXiggzg-YtkAYs_wL4jMMkxth5R2hkDR4Q2qIyTQbQ6PHX9IZlvZ2XCEu1pA0ZQCZJ1LKBvRQSd3VYXnqDZ4j2rjOjl1Vl5y0cr24dHVdlXkZek6K4pkHHuUe5Qw4qmqVdOKvKhdvGfegbP3nnFmpxXdbi2xotOX3Kc8o8ilQdaLTkRRN2BykqSnwgwWQL4_3n--3gicYvj52i8GPqAbkiksr5f3V7c3WOXlFw%2C%2C&v=8.2.1

Extremely long URL to avoid saving it on the website

\$\endgroup\$
3
  • \$\begingroup\$ I thought about bcmath too for this one, but didn't find how to enable it on TIO :D too bad there's trimmings to do \$\endgroup\$
    – Kaddath
    Feb 9 at 8:38
  • \$\begingroup\$ @Kaddath I wanted to try with gmp_add but I couldn't find anywhere with it enabled. Interestingly, the trimming is way shorter than removing with regular expressions. \$\endgroup\$ Feb 9 at 20:39
  • 1
    \$\begingroup\$ Yeah I looked at printf but couldn't make it work with the last case in less that 2 trims \$\endgroup\$
    – Kaddath
    Feb 10 at 16:40
2
\$\begingroup\$

Dyalog APL, 7 bytes

+/⍎¨

Try it online!

Explanation:

+/⍎¨
   ¨  ⍝ Map each element in the array
  ⍎   ⍝ Execute expression, which converts from string to integer
 /    ⍝ Reduce 
+     ⍝ Sum
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Welcome to CGCC. It looks like this code has problems with precision: for the last test case it returns 1E12 instead of the expected result (999999999999.999999999999). \$\endgroup\$
    – Dingus
    Feb 8 at 4:13
  • \$\begingroup\$ Thanks @Dingus! You are right that the last test case doesn't display as expected, but that has nothing to do with the solution. All it does is evaluate the expression as if we typed it ourselves, so we are only limited by what APL can do by default. Apparently double precision is not enough for this calculation, but we can get around that by using quad precision and making the formatting explicit to avoid scientific notation. I left it out because it looks a bit cumbersome, but I'll edit it for completeness. \$\endgroup\$
    – vcoutasso
    Feb 8 at 14:33
2
\$\begingroup\$

bc, 162 bytes

x=a+b;y=x/1;if(y==x)x=y;if(x==0||x<=-1||x>=1){print x,"\n";return}
if(scale(x)>1&&x*10^length(x)%10==0){scale=1;y=x/1.0;x=y};if(x<0)print "-0",-x else print "0",x

Try it online!. Adapted from a great s.o answer.

Explanation

x=a+b                       starting out simple
y=x/1;if(y==x)x=y           drop trailing 0's after the decimal
                            (99.00 -->  99 )
if(x==0||x<=-1||x>=1)       if x==0 or abs(x)>=1 then just print it
return                      preferable to `else{...}`, IMO. 
                            From now on x is between -1 and 1.
if(scale(x)>1               if x has an gnarly decimal
  &&x*10^length(x)%10==0)   and the last digit is 0
{scale=1;y=x/1.0;x=y}       drop trailing 0's & keep significant digits
                            (-.300 -->  -.3)
if(x<0)print "-0",-x        (-.3   --> -0.3)
else print "0",x            ( .5   -->  0.5)

\$\endgroup\$
2
\$\begingroup\$

JavaScript, 210 bytes

(a,b)=>([a,c='']=a.split`.`,[b,d='']=b.split`.`,m=c[k='length'],e=(o=(g=i=>BigInt(i+c.padEnd(l=m>d[k]?m:d[k],0)))(a)+g(b,c=d)+'')[k]-l,h=o.slice(0,e),i=o.slice(e).replace(/0+$/,''),(!h|h=='-'?h+0:h)+(i&&'.'+i))
(a,b)=>(                                    // Define function
  [a,c='']=a.split`.`,                      // a - integer part of number a
                                            // c - decimal part of number a (default value is '')

  [b,d='']=b.split`.`,                      // b - integer part of number b
                                            // d - decimal part of number b (default value is '')

  m=c[k='length'],                          // m - length of c
                                            // k - 'length' key

  e=(                                       // e - decimal point position
    o=                                      // o - sum of BigInts as string
      (g=i=>BigInt(                         // g - function which convert string to BigInt
                   i+
                   c.padEnd(
                        l=m>d[k]?m:d[k],    // l - the length of longest decimal part
                        0
                   )
            ))(a)+

                                            // c.padEnd(l, '0') - adding to the end of decimal
                                            // part as many zeros as it need to
                                            // be the length of 'l'`
                                            // i + c.padEnd(l, '0') - same number as in input
                                            // without decimal point and possibly
                                            // with trailing zeros

      g(b,c=d)+                             // now c is decimal part of number b

      ''                                    // added to convert BigInt to string
  )[k]-l,                               
  h=o.slice(0,e),                           // h - integer part of result

  i=o.slice(e).replace(/0+$/,''),           // i - decimal part of result

  (!h|h=='-'?h+0:h)                         // if h is empty string or minus
                                            // then add zero else do nothing

  +                                         // concatenate of integer part of result
                                            // and decimal part of result

  (i&&'.'+i)                                // if i is empty string then
                                            // return empty string else
)                                           // add leading dot

Try it:

f=(a,b)=>([a,c='']=a.split`.`,[b,d='']=b.split`.`,m=c[k='length'],e=(o=(g=i=>BigInt(i+c.padEnd(l=m>d[k]?m:d[k],0)))(a)+g(b,c=d)+'')[k]-l,h=o.slice(0,e),i=o.slice(e).replace(/0+$/,''),(!h|h=='-'?h+0:h)+(i&&'.'+i))

;[
    ['1', '1'], // '2'
    ['0', '0'], // '0'
    ['9', '1'], // '10'
    ['001.002', '3.400'], // '4.402'
    ['5', '-5.0'], // '0'
    ['2', '-2.5'], // '-0.5'
    ['12345', '67890'], // '80235'
    ['-50.6', '20.53'], // '-30.07'
    ['-001.00', '00100'], // '99'
    ['-00.100', '-0.20'], // '-0.3'
    ['0.1', '0.2'], // '0.3'
    ['1000000000000', '-0.000000000001'], // '999999999999.999999999999'
].map(x => {
    console.log(f(x[0], x[1]));
    console.log(f(x[1], x[0]));
})

\$\endgroup\$
0
1
\$\begingroup\$

Factor + decimals, 48 bytes

[ [ string>decimal ] bi@ D+ unparse " " split1 ]

enter image description here

[ string>decimal ] bi@   ! convert both inputs to the decimal data type (arbitrary size decimals)
D+                       ! add them together
unparse                  ! convert prettyprinted format to string
" " split1               ! remove DECIMAL: prefix
\$\endgroup\$
0
\$\begingroup\$

tcl:

expr $A+$B

Tcl has no concept of a "number"; everything is a string. Some strings can be interpreted as numbers, which is what "expr" does above. Literally "take the contents of the variables "A" and "B", put a "+" between the two and treat the result as interpretable as an expression (and return the result of that expression)"

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Numbers are strings up until they aren't, where in exprs they turn into floating point numbers, and thus have precision issues e.g. 0.1+0.2 \$\endgroup\$
    – Jo King
    Feb 8 at 1:15
  • 2
    \$\begingroup\$ Additionally, I don't see how this takes input or outputs? TCL has procedures and can also take input for stdin, submissions should use one of those \$\endgroup\$
    – Jo King
    Feb 8 at 1:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.