5
\$\begingroup\$

Given a number, say, -2982342342363.23425785 convert it to -2,982,342,342,363.23425785.

  • Extra points for not using look-behind

  • Extra points for not using regular expressions.

\$\endgroup\$

closed as off-topic by DJMcMayhem, Mego, wizzwizz4, nimi, Timtech Apr 30 '16 at 17:52

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions without an objective primary winning criterion are off-topic, as they make it impossible to indisputably decide which entry should win." – DJMcMayhem, Mego, wizzwizz4, nimi, Timtech
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    \$\begingroup\$ Why -2982,342,342,363.23425785 and not -2,982,342,342,363.23425785? \$\endgroup\$ – Howard Jan 3 '12 at 8:40
  • \$\begingroup\$ typo. fixed :-) \$\endgroup\$ – CamelCamelCamel Jan 3 '12 at 8:41
  • 3
    \$\begingroup\$ How many extra points for not using look-behind and regexp? What is the objective winning criteria? \$\endgroup\$ – user unknown Jan 4 '12 at 1:50
  • 8
    \$\begingroup\$ It is a bit strange to tag the challenge as regular-expression and give extra points if not using them... \$\endgroup\$ – PhiLho Jan 4 '12 at 9:25
  • 2
    \$\begingroup\$ This is not [code-golf] - is it? \$\endgroup\$ – user unknown Jan 6 '12 at 3:29

22 Answers 22

13
\$\begingroup\$

Java, 50 chars

String C(double d){return String.format("%,f",d);}

Works as specified in most locales. Does the correct thing in all locales :)

Edit:

If you really want commas no matter the default locale, do:

String C(double d){return String.format(java.util.Locale.US,"%,f",d);}

which is 70 characters (60 if you've already imported java.util.*).

\$\endgroup\$
  • \$\begingroup\$ Thus you might need to set a US locale to comply to the spec... :-) \$\endgroup\$ – PhiLho Jan 3 '12 at 15:37
  • \$\begingroup\$ Where does other locales differ? I remember vaguely a weird country that doesn't do the whole comma thing. \$\endgroup\$ – CamelCamelCamel Jan 4 '12 at 13:37
  • 1
    \$\begingroup\$ @Radagaisus: See en.wikipedia.org/wiki/Decimal_mark \$\endgroup\$ – Ilmari Karonen Jan 4 '12 at 15:59
4
\$\begingroup\$

Spreadsheet, 6+dec.places chars 5 chars

Just enter the number in a cell with formatting #,##0.#########,##0. Works on OOo Calc and MS Excel. Couldn't quickly find something meaning "print as many decimal places as there are". That would shorten it a lot. Can use an int, so the problem is not anymore.

No look-behind, no regex.

\$\endgroup\$
3
\$\begingroup\$

Scala:

val l=List (12345.2345, 123456.2345, 1234567.2345, -12345.2345, -123456.2345, -1234567.2345)

def f(d: Double)={
val a=d.toString.split('.')
(if(d<0)"-"else"")+
a(0).toLong.abs.toString.reverse.sliding(3,3).mkString(",").reverse+"."+a(1)}

l.map(f)

144 chars without 1st and last line, but this isn't code golf, is it?. Imho no look behind and no regex used.

Result:

List(12,345.2345, 123,456.2345, 1,234,567.2345, -12,345.2345, -123,456.2345, -1,234,567.2345)
\$\endgroup\$
  • \$\begingroup\$ I haven't run this so I may be wrong, but won't it give an error if the given number has no decimal point (because a will only have 1 item so a(1) will be referencing a non-existent index)? \$\endgroup\$ – Gareth Jan 4 '12 at 9:14
  • \$\begingroup\$ Since the number was not specified any further I assumed only numbers containing a decimal point, of different length and with and without negative sign. I did not consider pure Integers, hexadezimal notation, scientific notation (4.3e-23), numbers like '.4' or '-.4', '9.' or '-9.'. Numbers exceeding the range of Double aren't tested as well. However - since my function takes a Double as argument, an int-literal is implicitly converted to double, and f(4000) leads to 4,000.0 - but that is pure luck of mine. :) \$\endgroup\$ – user unknown Jan 4 '12 at 15:37
3
\$\begingroup\$

C 89/90/ANSI

#include <stdio.h>
#include <string.h>

int main(int i, char* a[]) {

    char * p;

    p    = strtok(a[1], ".");    /* Get argument value; up to decimal point. */
    a[1] = strtok(0,    ".");    /* Overwrite original value with fractional part. */

    /* If the first char is a negative sign then print it and advance the string pointer: */
    if(*p == '-') printf("%c", *p++);

    /* Get remainder of integer part divided by 3 and load it into a digit-group iterator: */
    i = strlen(p) % 3;

    if(i == 0) i = 3;    /* If iterator is empty then fill it. */

    while(1) {
        /* Nested loop through 3-digit group; printing each char: */
        while(i-- > 0) printf("%c", *p++);

        /* If string pointer is at null (end of string) char then exit loop: */
        if(*p == 0) break;
        else printf(",");    /* Otherwise print a comma */

        i = 3;    /* and reload the digit-group iterator. */
    }

    /* If there is a fractional part then print it after a decimal point: */
    if(a[1] != 0) printf(".%s", a[1]);  

    return 0;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to PPCG! \$\endgroup\$ – NoOneIsHere Apr 30 '16 at 2:59
  • \$\begingroup\$ Thanks! Nice to be here. \$\endgroup\$ – veganaiZe May 5 '16 at 2:17
2
\$\begingroup\$

Perl, regular expression

Using a regular expression and no look-behinds but look-aheads:

$v = "-2982342342363.23425785";
$v =~ s/\d(?=(\d{3})+(\.|$))/$&,/g;
print $v;

Each digit is replaced by itself plus a comma if it is followed by n digits where n is a multiple of three and immedetiately followed by a period or the end of the string.

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

Haskell, 105

f(a:b:c:n:s)|n/='-'=a:b:c:',':f(n:s)
f s=s
r=reverse
main=interact$uncurry((++).r.f.r).break(`elem`".\n")

This doesn't use regular expressions (obviously), but it does backtrack (namely, it reverses digits before adding separators).

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

Ruby 111 chars

parts = "-2982342342363.23425785".split('.')
parts[0].reverse.scan(/.{1,3}/).join(",").reverse + "." + parts[1]
\$\endgroup\$
2
\$\begingroup\$

PHP

Using the built in function for formatting numbers:

function format($number)
{
    $decimals=(strpos($number,'.')===false?0:strlen($number)-strpos($number,'.')-1);
    return number_format($number,$decimals,'.',',');
}

Doing it by hand:

function format($number)
{
    $parts=explode('.',$number);
    $temp="";
    $len=strlen($parts[0]);
    for($i=0;$i<$len;$i++)
    {
        if($i!=0 && $i%3==0)
        {
            $temp.=",".$parts[0][$len-$i-1];
        }
        else
        {
            $temp.=$parts[0][$len-$i-1];
        }
    }
    $parts[0]=strrev($temp);
    return implode('.',$parts);
}
\$\endgroup\$
  • \$\begingroup\$ That large for loop could probably be shortened a good bit if you use str_split. \$\endgroup\$ – Mr. Llama Jan 10 '12 at 22:33
2
\$\begingroup\$

PHP, 56

No Regex, no look-behind.

function f($n){return number_format($n).strstr($n,'.');}


My first attempts tried calculating how many decimals to keep in order to use it as the optional second parameter for number_format but I couldn't think of concise logic.

My second attempt treated the number as a string, splitting on ., then re-assembling it.

Then I realized I could just let number_format truncate for me, then re-append the decimal section (if any) with strstr.


A different version, not using number_format, in a total of 95 bytes:

function f($n){return strrev(implode(',',str_split(strrev(strtok($n,'.')),3))).strstr($n,'.');}

Longer and a bit more readable:

function f($num) {
    $right = strstr($num, '.');

    $left = strtok($num, '.');
    $left = str_split(strrev($left), 3);
    $left = strrev(implode(',', $left));

    return $left . $right;
}
\$\endgroup\$
  • \$\begingroup\$ No need for character counts, this hasn't been tagged as [code-golf]. str_split never even occurred to me. \$\endgroup\$ – Gareth Jan 10 '12 at 22:45
  • \$\begingroup\$ Ah, kinda near here. Just assumed that because this was the codegolf stackexchange that everything was code golf. Oh well, it was good practice. \$\endgroup\$ – Mr. Llama Jan 10 '12 at 22:53
  • \$\begingroup\$ 'Programming puzzles and Code Golf'. Not to worry, most people have made the same assumption on this question. :-) \$\endgroup\$ – Gareth Jan 10 '12 at 22:56
2
\$\begingroup\$

vi, 25 chars

ebd0$F.99@='3hha,<C-V><ESC>'
0PZZ

The script moves the cursor to the last digit of the number, then tries to search backwards for a decimal point. From there (either the last digit, or the decimal point if there was one), the following process is executed 99 times: the cursor moves left 3 times, then attempts to move left again. If it was able to move left, then a comma is inserted; otherwise, the process aborts.

9 times is probably sufficient instead of 99, so this could be reduced to 18 24 characters.

EDIT: I realized that I wasn't accounting for possible edge cases with a negative sign in front, so I added six characters to deal with it. At the start, 'eb' will effectively move the cursor to the first digit of the number. 'd0' will delete everything before that (which is either the negative sign or nothing). Then at the end, '0' moves to the start of the line and 'P' inserts whatever was deleted earlier.

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

Python2 - 219

def c(s):
 c=str(s)
 i=max([k for k in range(len(c)) if c[k]=='.']+[-1])
 o=(c[i::] if i>0 else '')
 if i>0: c=c[:i]
 c=list(c);i=0;m={0:1,1:2,2:3,3:1}
 while len(c):
  o=c.pop()+(',' if i==2 else '')+o;i=m[i]
 return o

or I can do this...

Python2 - 112

def c(s):
 import locale
 locale.setlocale(locale.LC_ALL, 'en_US')
 return locale.format("%d", s, grouping=True)

Personally I like the first one more... mainly because of the map instead of logic hack. The second one is verbatim from StackOverflow of all places.

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

Python 2.7, 127/122 bytes

taking input from stndin:

n=`input()*1.0`.split('.')
s=''
k=0
for y in n[0][::-1]:
 s+=y;k+=1
 if k in range(0,len(n[0]),3):s+=','
print s[::-1]+'.'+n[1]

as a function:

def c(n):
 s='';k=0
 for y in`n*1.0`[0][::-1]:
  s+=y;k+=1
  if k in range(0,len(n[0]),3):s+=','
  return s[::-1]+'.'+n[1]

could probably be golfed more...

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

In Lua:

local lns =
{
"-2982342342363.23425785",
"-2982342342363",
"2982342342363",
"-.23425785",
".23425785",
"1",
"22",
"-333",
"4444.",
"333.333",
"puke",
}
local results = {} -- Find converted strings here
for i, ns in ipairs(lns) do
  local sign, digits, frac = string.match(ns, "^(%-?)(%d-)(%.%d*)$")
  if frac == nil then
    frac, sign, digits = '', string.match(ns, "^(%-?)(%d-)$")
  end
  if digits == nil or digits == '' then
    results[i] = ns -- Only a fractional part (or no numbers), leave unchanged
  else
    local groups = {}
    local v = 0 + digits
    while v > 0 do
      local g = math.fmod(v, 1000)
      table.insert(groups, 1, g)
      v = math.floor(v / 1000)
    end
    results[i] = sign .. table.concat(groups, ",") .. frac
  end
end

Odd mix of regular expressions and arithmetic... It allows to handle non-numbers, sign, etc. in a quite concise way. I need the two steps RE as they are quite limited in Lua.

[EDIT] I don't really comply to the requirement as I made an array of strings instead of an array of numbers. On the other hand, by default Lua is compiled with 32bit floats, not 64bit doubles, so the given number is truncated to the first digit after the decimal point... So Lua remains out of the competition. Oh well, it was fun to play with Lua again.

\$\endgroup\$
  • \$\begingroup\$ And technically speaking, those are Lua Patterns not RegEx :D \$\endgroup\$ – Dalin Seivewright Jan 11 '12 at 18:10
1
\$\begingroup\$

num.replace(/(\d+)(\..*)?/, ($0,$1,$2) -> $1.replace(/(\d)(?=(\d{3})+$)/g,'$1,') + ($2 || ''))

This is in coffee script. There's no look behind in JavaScript, but you can pass a function to .replace(!). The first regex separates the string over the dot and the second regex deals only with the first part. It's a shorter way than using .split() and I had problems mimicking look-behind.

edit: changed to work in webkit browsers.

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

Python 3: 100 or 102 characters

100 characters as a function:

def f(s):n=(s+'.').find('.');return''.join((('-'in s)<i<n and i%3==n%3)*','+c for i,c in enumerate(s))

102 reading from stdin:

s=input();n=(s+'.').find('.');print(''.join((('-'in s)<i<n and i%3==n%3)*','+c for i,c in enumerate(s)))
\$\endgroup\$
1
\$\begingroup\$

Python (162 chars)

a=raw_input().split('.')
n=int(a[0])
a[0]=str(n%1000)
n=n/1000
while n>0:
    a[0]=str(n%1000)+','+a[0]
    n=n/1000
if len(a)>1:
    print a[0]+'.'+a[1]
else:
    print a[0]

Gah! New specs ruin this solution. Well for integer input

(89 chars)

n=int(raw_input())
s=str(n%1000)
n/=1000
while n>0:
    s=str(n%1000)+','+s
    n/=1000
print s

Will put up one for float later.

For float input

(152 97 chars)

n=float(raw_input())
s=str(n%1000)
n=int(n/1000)
while n>0:
    s=str(n%1000)+','+s
    n/=1000
print s
\$\endgroup\$
1
\$\begingroup\$

Python 2.7: 29 characters

A bit late to the party, but I just read about this new feature in 2.7 and figured this still deserves to be here:

f=lambda(n):'{:,f}'.format(n)

Used by calling f(-2982342342363.23425785) which returns "-2,982,342,342,363.234375". The error is due to floating-point precision: -2982342342363.23425785 gets represented as -2982342342363.234375.

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

C (99)

No regexes, no backtracking, not using built-in formatting of real numbers.

d=1000;f(x){x&&f(x/d)+printf("%d,",x%d);}main(x,y){scanf("%d.%d",&x,&y);f(x);printf("%c.%d",8,y);}
\$\endgroup\$
1
\$\begingroup\$

Q, 27 chars (integer input)

{(|:)","sv 3 cut (|:) -3!x}

q){(|:)","sv 3 cut (|:) -3!x}[23498723]

"23,498,723"

Q, 48 chars (float input)

{,[;".",a@1](|:)","sv 3 cut(|:)(*:)a:"."vs -3!x}

q){,;".",a@1","sv 3 cut(|:)(*:)a:"." vs -3!x}[234554.21434]

"234,554.21434"

\$\endgroup\$
  • \$\begingroup\$ @sinedcm suggested that you can get it down to 24 as {(|:)","sv 3 cut(|)($)x} \$\endgroup\$ – Peter Taylor Mar 23 '12 at 19:48
0
\$\begingroup\$

Some JavaScript approaches:

(1) 74 characters but fails on some inputs, recursive creation:

function r(n){return n<0?"-"+r(-n):n<1e3?""+n:r(n/1e3-n/1e3%1)+","+n%1e3}

It has two failure channels: (1) if the substring contains subnumbers with leading 0's it sometimes will produce e.g. 1,10 instead of 1,010, and (2) I don't actually know if n/1000 - (n/1000)%1 always yields an integer. With bugs corrected the smallest I can get it is 130 characters:

function r(n,f){f=Math.floor;return n<0?"-"+r(-n):n%1?r(f(n))+(""+n).match(/\..*/)[0]:n<1e3?""+n:r(f(n/1e3))+","+(""+n).slice(-3)}

(2) Split over lookahead and join with commas, 95 characters, one statement:

m=(""+n).split('.'),m[0].split(/(?=(?:.{3})+$)/).join(",").replace("-,","-")+(m[1]?"."+m[1]:"")

I can't see a better way to handle negatives in this case.

(3) Array inserts every 3 characters to the left of the '.', 100 characters, uses a Bad Part of JS (n < 0 is true or false which coerce to 1 or 0 when used with <).

t=(""+n).split(""),i=t.indexOf('.');for(i=(i<0?t.length:i)-3;n<0<i;i-=3)t.splice(i,0,',');t.join("")
\$\endgroup\$
0
\$\begingroup\$

Ruby (didn't golf or count. No regexps.)

def commaize(n)
  a,b=((negative=n<0) ? -n : n).to_s.split('.')
  "#{negative ? '-':''}#{a.reverse.chars.each_slice(3).map(&:join).join(',').reverse}.#{b}"
end
\$\endgroup\$
0
\$\begingroup\$

COBOL

   ID DIVISION. 
   PROGRAM-ID. EDITNUM. 
   DATA DIVISION. 
   WORKING-STORAGE SECTION. 
   01  REALLY-BIG-NUMBER PIC --,---,---,---,--9.9(8). 
   PROCEDURE DIVISION. 
       MOVE -2982342342363.23425785 TO REALLY-BIG-NUMBER
       DISPLAY REALLY-BIG-NUMBER 
       GOBACK 
       . 

10 lines of code, of which six are mandatory. All the work is done by the MOVE statement, ably abetted by the data-definition.

The data-definition (01) uses a Picture-string to describe how the data is to be edited. Here, there will be a leading floating minus-sign, commas separating groups of three digits going left from (to-be-printed) decimal point. There are eight significant decimal places.

MOVE the value to the field, DISPLAY the field (if you want to see what the number looks like).

-2,982,342,342,363.23425785

\$\endgroup\$

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