Satisfying Rounding












16












$begingroup$


Satisfying Rounding



You know when you're in science class, and asked to round to 2 sig figs, but your answer is 5.2501...? You should round to 5.3, but that's just so unsatisfying! By rounding to 5.3, you're off by a whole 0.05, which is a large amount compared to 0.1 (the place value you're rounding to)! So help me round in a satisfying way.



To round in a satisfying way, you must round at the first digit you come across that produces a relatively small error - less than half of the maximum error possible when rounding. Basically, you need to round whenever you encounter 0, 1, 8, or 9. If that never happens, return the input as is. Do not round on leading zeroes or ones - that just doesn't feel satisfying.



Input



A string or float value that represents a nonnegative decimal number.



Output



The same decimal number rounded satisfactorily, in either string or float format.



Examples



Input -> Output
0 -> 0
0.5 -> 0.5
0.19 -> 0
0.8 -> 1
5.64511 -> 5.645
18.913 -> 20
88.913 -> 100
36.38299 -> 36.4
621 -> 620
803.22 -> 1000
547.4726 -> 547.4726


This is a code-golf challenge, so shortest code wins!










share|improve this question











$endgroup$












  • $begingroup$
    Sandbox
    $endgroup$
    – Quintec
    Dec 8 '18 at 15:11










  • $begingroup$
    Are strings such as 036.40000 considered a valid output?
    $endgroup$
    – Arnauld
    Dec 8 '18 at 16:08






  • 1




    $begingroup$
    Can we assume that a .0 part will be given for integers? Also, 0 isn't positive.
    $endgroup$
    – Erik the Outgolfer
    Dec 8 '18 at 16:08










  • $begingroup$
    @EriktheOutgolfer No, you may not - also thanks, changed to nonnegative.
    $endgroup$
    – Quintec
    Dec 8 '18 at 17:03






  • 1




    $begingroup$
    So 19 rounds to 20 but 0.19 rounds to 0? Why?
    $endgroup$
    – Neil
    Dec 8 '18 at 19:38
















16












$begingroup$


Satisfying Rounding



You know when you're in science class, and asked to round to 2 sig figs, but your answer is 5.2501...? You should round to 5.3, but that's just so unsatisfying! By rounding to 5.3, you're off by a whole 0.05, which is a large amount compared to 0.1 (the place value you're rounding to)! So help me round in a satisfying way.



To round in a satisfying way, you must round at the first digit you come across that produces a relatively small error - less than half of the maximum error possible when rounding. Basically, you need to round whenever you encounter 0, 1, 8, or 9. If that never happens, return the input as is. Do not round on leading zeroes or ones - that just doesn't feel satisfying.



Input



A string or float value that represents a nonnegative decimal number.



Output



The same decimal number rounded satisfactorily, in either string or float format.



Examples



Input -> Output
0 -> 0
0.5 -> 0.5
0.19 -> 0
0.8 -> 1
5.64511 -> 5.645
18.913 -> 20
88.913 -> 100
36.38299 -> 36.4
621 -> 620
803.22 -> 1000
547.4726 -> 547.4726


This is a code-golf challenge, so shortest code wins!










share|improve this question











$endgroup$












  • $begingroup$
    Sandbox
    $endgroup$
    – Quintec
    Dec 8 '18 at 15:11










  • $begingroup$
    Are strings such as 036.40000 considered a valid output?
    $endgroup$
    – Arnauld
    Dec 8 '18 at 16:08






  • 1




    $begingroup$
    Can we assume that a .0 part will be given for integers? Also, 0 isn't positive.
    $endgroup$
    – Erik the Outgolfer
    Dec 8 '18 at 16:08










  • $begingroup$
    @EriktheOutgolfer No, you may not - also thanks, changed to nonnegative.
    $endgroup$
    – Quintec
    Dec 8 '18 at 17:03






  • 1




    $begingroup$
    So 19 rounds to 20 but 0.19 rounds to 0? Why?
    $endgroup$
    – Neil
    Dec 8 '18 at 19:38














16












16








16





$begingroup$


Satisfying Rounding



You know when you're in science class, and asked to round to 2 sig figs, but your answer is 5.2501...? You should round to 5.3, but that's just so unsatisfying! By rounding to 5.3, you're off by a whole 0.05, which is a large amount compared to 0.1 (the place value you're rounding to)! So help me round in a satisfying way.



To round in a satisfying way, you must round at the first digit you come across that produces a relatively small error - less than half of the maximum error possible when rounding. Basically, you need to round whenever you encounter 0, 1, 8, or 9. If that never happens, return the input as is. Do not round on leading zeroes or ones - that just doesn't feel satisfying.



Input



A string or float value that represents a nonnegative decimal number.



Output



The same decimal number rounded satisfactorily, in either string or float format.



Examples



Input -> Output
0 -> 0
0.5 -> 0.5
0.19 -> 0
0.8 -> 1
5.64511 -> 5.645
18.913 -> 20
88.913 -> 100
36.38299 -> 36.4
621 -> 620
803.22 -> 1000
547.4726 -> 547.4726


This is a code-golf challenge, so shortest code wins!










share|improve this question











$endgroup$




Satisfying Rounding



You know when you're in science class, and asked to round to 2 sig figs, but your answer is 5.2501...? You should round to 5.3, but that's just so unsatisfying! By rounding to 5.3, you're off by a whole 0.05, which is a large amount compared to 0.1 (the place value you're rounding to)! So help me round in a satisfying way.



To round in a satisfying way, you must round at the first digit you come across that produces a relatively small error - less than half of the maximum error possible when rounding. Basically, you need to round whenever you encounter 0, 1, 8, or 9. If that never happens, return the input as is. Do not round on leading zeroes or ones - that just doesn't feel satisfying.



Input



A string or float value that represents a nonnegative decimal number.



Output



The same decimal number rounded satisfactorily, in either string or float format.



Examples



Input -> Output
0 -> 0
0.5 -> 0.5
0.19 -> 0
0.8 -> 1
5.64511 -> 5.645
18.913 -> 20
88.913 -> 100
36.38299 -> 36.4
621 -> 620
803.22 -> 1000
547.4726 -> 547.4726


This is a code-golf challenge, so shortest code wins!







code-golf number rational-numbers






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 9 '18 at 20:42







Quintec

















asked Dec 8 '18 at 15:10









QuintecQuintec

1,4881722




1,4881722












  • $begingroup$
    Sandbox
    $endgroup$
    – Quintec
    Dec 8 '18 at 15:11










  • $begingroup$
    Are strings such as 036.40000 considered a valid output?
    $endgroup$
    – Arnauld
    Dec 8 '18 at 16:08






  • 1




    $begingroup$
    Can we assume that a .0 part will be given for integers? Also, 0 isn't positive.
    $endgroup$
    – Erik the Outgolfer
    Dec 8 '18 at 16:08










  • $begingroup$
    @EriktheOutgolfer No, you may not - also thanks, changed to nonnegative.
    $endgroup$
    – Quintec
    Dec 8 '18 at 17:03






  • 1




    $begingroup$
    So 19 rounds to 20 but 0.19 rounds to 0? Why?
    $endgroup$
    – Neil
    Dec 8 '18 at 19:38


















  • $begingroup$
    Sandbox
    $endgroup$
    – Quintec
    Dec 8 '18 at 15:11










  • $begingroup$
    Are strings such as 036.40000 considered a valid output?
    $endgroup$
    – Arnauld
    Dec 8 '18 at 16:08






  • 1




    $begingroup$
    Can we assume that a .0 part will be given for integers? Also, 0 isn't positive.
    $endgroup$
    – Erik the Outgolfer
    Dec 8 '18 at 16:08










  • $begingroup$
    @EriktheOutgolfer No, you may not - also thanks, changed to nonnegative.
    $endgroup$
    – Quintec
    Dec 8 '18 at 17:03






  • 1




    $begingroup$
    So 19 rounds to 20 but 0.19 rounds to 0? Why?
    $endgroup$
    – Neil
    Dec 8 '18 at 19:38
















$begingroup$
Sandbox
$endgroup$
– Quintec
Dec 8 '18 at 15:11




$begingroup$
Sandbox
$endgroup$
– Quintec
Dec 8 '18 at 15:11












$begingroup$
Are strings such as 036.40000 considered a valid output?
$endgroup$
– Arnauld
Dec 8 '18 at 16:08




$begingroup$
Are strings such as 036.40000 considered a valid output?
$endgroup$
– Arnauld
Dec 8 '18 at 16:08




1




1




$begingroup$
Can we assume that a .0 part will be given for integers? Also, 0 isn't positive.
$endgroup$
– Erik the Outgolfer
Dec 8 '18 at 16:08




$begingroup$
Can we assume that a .0 part will be given for integers? Also, 0 isn't positive.
$endgroup$
– Erik the Outgolfer
Dec 8 '18 at 16:08












$begingroup$
@EriktheOutgolfer No, you may not - also thanks, changed to nonnegative.
$endgroup$
– Quintec
Dec 8 '18 at 17:03




$begingroup$
@EriktheOutgolfer No, you may not - also thanks, changed to nonnegative.
$endgroup$
– Quintec
Dec 8 '18 at 17:03




1




1




$begingroup$
So 19 rounds to 20 but 0.19 rounds to 0? Why?
$endgroup$
– Neil
Dec 8 '18 at 19:38




$begingroup$
So 19 rounds to 20 but 0.19 rounds to 0? Why?
$endgroup$
– Neil
Dec 8 '18 at 19:38










7 Answers
7






active

oldest

votes


















2












$begingroup$

JavaScript (ES6),  100 99 98  78 bytes



Takes input as a string. Returns a float.





s=>+(0+s).replace(/d/g,(d,i)=>j&&+d+((n=s[i+!++s[i]])<2&&i?--j:n>7&&j--),j=1)


Try it online!



How?



We first prepend a leading $0$ to the input string, so that we're guaranteed to have a digit before a possible leading $8$ or $9$, that must trigger the rounding right away.



The flag $j$ is set to $1$ as long as we are looking for a digit on which we can do a satisfying rounding, and set to $0$ afterwards.



Because a leading $0$ was added to the string that we're walking through but $s$ was left unchanged, $d$ contains the current character and $s[i]$ is pointing to the next character.



We use the following code to load the next digit in $n$, skipping a possible decimal separator:



n = s[i + !++s[i]]


Although strings are immutable in JavaScript, the expression ++s[i] will return $s[i]+1$ if it contains a numeric value, even though $s[i]$ is not actually incremented. Therefore, the expression !++s[i] is evaluated to $false$ (coerced to $0$) for all digits (including $0$) and to $true$ (coerced to $1$) for the decimal separator ".".



When the rounding occurs, we yield d + --j if the next digit $n$ is $0$ or $1$ (and it's not the leading digit of the original input) and d + j-- if $n$ is $8$ or $9$. Therefore, $j$ is set to $0$ in both cases but we add $0$ to $d$ in the first case (rounding down) and $1$ in the second case (rounding up).






share|improve this answer











$endgroup$









  • 1




    $begingroup$
    And the pinball/rubber ball falls into a ditch! :)
    $endgroup$
    – Quintec
    Dec 8 '18 at 20:17



















2












$begingroup$


Ruby, 79 77 69 67 65 bytes





->n,z=n+".0"{z[i=z=~/./]='';n.to_f.round (z=~/(?!^)[01]|8|9/)-i}


Try it online!



Explanation





  • ->n Take input as a string


  • z=n+".0" Create a temporary string z that is guaranteed to contain a dot and a relevant digit.


  • i=z=~/./ Determine the position of the decimal dot in z and assign to i.


  • z[i]='' Drop the dot so that it doesn't get in the way further on.


  • z=~/(?!^)[01]|8|9/ Determine the position of non-starting 0-1 or any 8-9, whichever comes first.


  • (...)-i This difference will be the number of decimal places to keep, negative if we will be rounding left of the dot.


  • n.to_f.round ... Convert to float and do the rounding.






share|improve this answer











$endgroup$





















    1












    $begingroup$


    Jelly, 34 bytes



    ;”.ḟ$µ»"”2e€⁽¡XṾ¤;1i1_i”.$_>¥0ɓVær


    Try it online!



    -1 thanks to Jonathan Allan.






    share|improve this answer











    $endgroup$













    • $begingroup$
      Why ŒV? I think V will work too.
      $endgroup$
      – Jonathan Allan
      Dec 8 '18 at 18:15










    • $begingroup$
      @JonathanAllan Nope. (basically banker's rounding quirks)
      $endgroup$
      – Erik the Outgolfer
      Dec 8 '18 at 18:16












    • $begingroup$
      Oh, because it's not acting on the input? Try _>¥0ɓVær like mine is (I missed usage of the dyadic quick so thanks too!)
      $endgroup$
      – Jonathan Allan
      Dec 8 '18 at 18:24










    • $begingroup$
      @JonathanAllan Ah, clever usage of chains, thanks.
      $endgroup$
      – Erik the Outgolfer
      Dec 8 '18 at 18:27



















    1












    $begingroup$


    Jelly,  30  29 bytes



    -1 thanks to Erik the Outgolfer (use of dyadic quick ¥ from his answer)



    O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær


    A monadic link accepting a list of characters which yields a float.



    Try it online! Or see the test-suite.



    How



    First note that the input string is made exclusively from the characters 0123456789. which have ordinals [48,49,50,51,52,53,54,55,56,57,46], which have remainders when divided by eight of [0,1,2,3,4,5,6,7,0,1,6]. The only characters which are between -1 and 1 inclusive are 0, 1, 8, and 9.



    Furthermore if we subtract eight from the ordinals ([40,41,42,43,44,45,46,47,48,49,38]) the same (fairly obviously) holds. If we halve these ([20,20.5,21,21.5,22,22.5,23,23.5,24,24.5,19]) the only characters which have remainders when divided by eight which are between -1 and 1 inclusive are 8 and 9.



    O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær - Link: list of characters, S
    O - ordinal (vectorises across S)
    ;0 - concatenate a zero
    - (to cater BOTH for no '0', '1', '8', or '9' AND for no '.')
    µ - start a new monadic link (call that X)
    _8 - subtract eight (vectorises across X)
    ¦ - sparse application...
    1 - ...to: indices: one
    H - ...do: halve (i.e. halve first ordinal)
    %8 - modulo by eight (vectorises)
    Ị - insignificant (abs(v)<=1?) (vectorises)
    T - truthy indices
    Ḣ - head
    Ɗ - last three links as a monad (i.e. f(X)):
    <48 - less than 48? (i.e. was it a '.' in S or the added 0?)
    T - truthy indices
    Ḣ - head
    _ - subtract
    ¥ - last two links as a dyad
    < 0 - less than zero? (1 if so 0 otherwise)
    _ - subtract
    ɓ - start a new dyadic chain (i.e. f(S,X))
    V - evaluate S as Jelly code (i.e. get S as a float)
    ær - round to the nearest multiple of 10^(-X)





    share|improve this answer











    $endgroup$





















      1












      $begingroup$


      Retina 0.8.2, 75 bytes



      ^[89]
      10
      T`d`0`(?<=.)[01].*|(?<=8|9).*
      T`89d`0d`..?[89]
      (.|(..+?))0+$
      $2


      Try it online! Link includes test cases. Explanation:



      ^[89]
      10


      Handle the case of a leading 8 or 9.



      T`d`0`(?<=.)[01].*|(?<=8|9).*


      If there's a non-leading 0 or 1, then zero it and the rest of the string out. Also, if there's an 8 or 9, then leave it, but zero out the rest of the string. (But leave the decimal point unchanged in either case.)



      T`89d`0d`..?[89]


      If there's still an 8 or a 9 at this point, then zero it out, and increment the preceding digit (possibly before the decimal point).



      (.|(..+?))0+$
      $2


      Delete trailing zeros if they are after a decimal point, but only delete the decimal point if there are no other digits in between.






      share|improve this answer









      $endgroup$





















        1












        $begingroup$


        C (gcc), 111 102 bytes





        g(_,i,j,k)char*_;{for(i=*_<56?*_++:48,j=3;j;j&=k%8>1|(i=*_++)/48*2)putchar(j&1?i+(k=_[*_<48])/56:48);}


        Try it online!



        //_: input, as string
        //i: current digit, rounded if applicable
        //j: tracks whether number is rounded, and whether or '.' has been encountered
        //k: digit to test rounding (round if k is one of 0,1,8,9)
        //'0'==48, '8'==56
        g(_,i,j,k)char*_;{
        for(i=*_<56?*_++:48,j=3; //special case: if first digit is 8 or 9, use a
        //placeholder digit with value 0. initialize j.
        j; //only stop execution when number is rounded and
        //'.' or has been encountered.
        j&=k%8>1|(i=*_++)/48*2) //check if execution should stop.
        putchar(j&1?i+(k=_[*_<48])/56:48); //print '0' if rounding had already been done;
        //otherwise, print digit. round up as needed.
        }





        share|improve this answer











        $endgroup$





















          0












          $begingroup$


          C# (Visual C# Interactive Compiler), 280 bytes





          c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=decimal.Parse(c);Func<decimal>q=()=>(decimal)Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


          Try it online!



          It can be shorter if I used doubles instead of decimals, but I used decimals to preserve accuracy, or else a number like 547.4726 would be 547.472595214844.




          C# (Visual C# Interactive Compiler), 268 bytes





          c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=float.Parse(c);Func<double>q=()=>Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


          Try it online!(Less accurate version)






          share|improve this answer











          $endgroup$













            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "200"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f177203%2fsatisfying-rounding%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            7 Answers
            7






            active

            oldest

            votes








            7 Answers
            7






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            2












            $begingroup$

            JavaScript (ES6),  100 99 98  78 bytes



            Takes input as a string. Returns a float.





            s=>+(0+s).replace(/d/g,(d,i)=>j&&+d+((n=s[i+!++s[i]])<2&&i?--j:n>7&&j--),j=1)


            Try it online!



            How?



            We first prepend a leading $0$ to the input string, so that we're guaranteed to have a digit before a possible leading $8$ or $9$, that must trigger the rounding right away.



            The flag $j$ is set to $1$ as long as we are looking for a digit on which we can do a satisfying rounding, and set to $0$ afterwards.



            Because a leading $0$ was added to the string that we're walking through but $s$ was left unchanged, $d$ contains the current character and $s[i]$ is pointing to the next character.



            We use the following code to load the next digit in $n$, skipping a possible decimal separator:



            n = s[i + !++s[i]]


            Although strings are immutable in JavaScript, the expression ++s[i] will return $s[i]+1$ if it contains a numeric value, even though $s[i]$ is not actually incremented. Therefore, the expression !++s[i] is evaluated to $false$ (coerced to $0$) for all digits (including $0$) and to $true$ (coerced to $1$) for the decimal separator ".".



            When the rounding occurs, we yield d + --j if the next digit $n$ is $0$ or $1$ (and it's not the leading digit of the original input) and d + j-- if $n$ is $8$ or $9$. Therefore, $j$ is set to $0$ in both cases but we add $0$ to $d$ in the first case (rounding down) and $1$ in the second case (rounding up).






            share|improve this answer











            $endgroup$









            • 1




              $begingroup$
              And the pinball/rubber ball falls into a ditch! :)
              $endgroup$
              – Quintec
              Dec 8 '18 at 20:17
















            2












            $begingroup$

            JavaScript (ES6),  100 99 98  78 bytes



            Takes input as a string. Returns a float.





            s=>+(0+s).replace(/d/g,(d,i)=>j&&+d+((n=s[i+!++s[i]])<2&&i?--j:n>7&&j--),j=1)


            Try it online!



            How?



            We first prepend a leading $0$ to the input string, so that we're guaranteed to have a digit before a possible leading $8$ or $9$, that must trigger the rounding right away.



            The flag $j$ is set to $1$ as long as we are looking for a digit on which we can do a satisfying rounding, and set to $0$ afterwards.



            Because a leading $0$ was added to the string that we're walking through but $s$ was left unchanged, $d$ contains the current character and $s[i]$ is pointing to the next character.



            We use the following code to load the next digit in $n$, skipping a possible decimal separator:



            n = s[i + !++s[i]]


            Although strings are immutable in JavaScript, the expression ++s[i] will return $s[i]+1$ if it contains a numeric value, even though $s[i]$ is not actually incremented. Therefore, the expression !++s[i] is evaluated to $false$ (coerced to $0$) for all digits (including $0$) and to $true$ (coerced to $1$) for the decimal separator ".".



            When the rounding occurs, we yield d + --j if the next digit $n$ is $0$ or $1$ (and it's not the leading digit of the original input) and d + j-- if $n$ is $8$ or $9$. Therefore, $j$ is set to $0$ in both cases but we add $0$ to $d$ in the first case (rounding down) and $1$ in the second case (rounding up).






            share|improve this answer











            $endgroup$









            • 1




              $begingroup$
              And the pinball/rubber ball falls into a ditch! :)
              $endgroup$
              – Quintec
              Dec 8 '18 at 20:17














            2












            2








            2





            $begingroup$

            JavaScript (ES6),  100 99 98  78 bytes



            Takes input as a string. Returns a float.





            s=>+(0+s).replace(/d/g,(d,i)=>j&&+d+((n=s[i+!++s[i]])<2&&i?--j:n>7&&j--),j=1)


            Try it online!



            How?



            We first prepend a leading $0$ to the input string, so that we're guaranteed to have a digit before a possible leading $8$ or $9$, that must trigger the rounding right away.



            The flag $j$ is set to $1$ as long as we are looking for a digit on which we can do a satisfying rounding, and set to $0$ afterwards.



            Because a leading $0$ was added to the string that we're walking through but $s$ was left unchanged, $d$ contains the current character and $s[i]$ is pointing to the next character.



            We use the following code to load the next digit in $n$, skipping a possible decimal separator:



            n = s[i + !++s[i]]


            Although strings are immutable in JavaScript, the expression ++s[i] will return $s[i]+1$ if it contains a numeric value, even though $s[i]$ is not actually incremented. Therefore, the expression !++s[i] is evaluated to $false$ (coerced to $0$) for all digits (including $0$) and to $true$ (coerced to $1$) for the decimal separator ".".



            When the rounding occurs, we yield d + --j if the next digit $n$ is $0$ or $1$ (and it's not the leading digit of the original input) and d + j-- if $n$ is $8$ or $9$. Therefore, $j$ is set to $0$ in both cases but we add $0$ to $d$ in the first case (rounding down) and $1$ in the second case (rounding up).






            share|improve this answer











            $endgroup$



            JavaScript (ES6),  100 99 98  78 bytes



            Takes input as a string. Returns a float.





            s=>+(0+s).replace(/d/g,(d,i)=>j&&+d+((n=s[i+!++s[i]])<2&&i?--j:n>7&&j--),j=1)


            Try it online!



            How?



            We first prepend a leading $0$ to the input string, so that we're guaranteed to have a digit before a possible leading $8$ or $9$, that must trigger the rounding right away.



            The flag $j$ is set to $1$ as long as we are looking for a digit on which we can do a satisfying rounding, and set to $0$ afterwards.



            Because a leading $0$ was added to the string that we're walking through but $s$ was left unchanged, $d$ contains the current character and $s[i]$ is pointing to the next character.



            We use the following code to load the next digit in $n$, skipping a possible decimal separator:



            n = s[i + !++s[i]]


            Although strings are immutable in JavaScript, the expression ++s[i] will return $s[i]+1$ if it contains a numeric value, even though $s[i]$ is not actually incremented. Therefore, the expression !++s[i] is evaluated to $false$ (coerced to $0$) for all digits (including $0$) and to $true$ (coerced to $1$) for the decimal separator ".".



            When the rounding occurs, we yield d + --j if the next digit $n$ is $0$ or $1$ (and it's not the leading digit of the original input) and d + j-- if $n$ is $8$ or $9$. Therefore, $j$ is set to $0$ in both cases but we add $0$ to $d$ in the first case (rounding down) and $1$ in the second case (rounding up).







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Dec 9 '18 at 17:26

























            answered Dec 8 '18 at 16:06









            ArnauldArnauld

            73.5k689309




            73.5k689309








            • 1




              $begingroup$
              And the pinball/rubber ball falls into a ditch! :)
              $endgroup$
              – Quintec
              Dec 8 '18 at 20:17














            • 1




              $begingroup$
              And the pinball/rubber ball falls into a ditch! :)
              $endgroup$
              – Quintec
              Dec 8 '18 at 20:17








            1




            1




            $begingroup$
            And the pinball/rubber ball falls into a ditch! :)
            $endgroup$
            – Quintec
            Dec 8 '18 at 20:17




            $begingroup$
            And the pinball/rubber ball falls into a ditch! :)
            $endgroup$
            – Quintec
            Dec 8 '18 at 20:17











            2












            $begingroup$


            Ruby, 79 77 69 67 65 bytes





            ->n,z=n+".0"{z[i=z=~/./]='';n.to_f.round (z=~/(?!^)[01]|8|9/)-i}


            Try it online!



            Explanation





            • ->n Take input as a string


            • z=n+".0" Create a temporary string z that is guaranteed to contain a dot and a relevant digit.


            • i=z=~/./ Determine the position of the decimal dot in z and assign to i.


            • z[i]='' Drop the dot so that it doesn't get in the way further on.


            • z=~/(?!^)[01]|8|9/ Determine the position of non-starting 0-1 or any 8-9, whichever comes first.


            • (...)-i This difference will be the number of decimal places to keep, negative if we will be rounding left of the dot.


            • n.to_f.round ... Convert to float and do the rounding.






            share|improve this answer











            $endgroup$


















              2












              $begingroup$


              Ruby, 79 77 69 67 65 bytes





              ->n,z=n+".0"{z[i=z=~/./]='';n.to_f.round (z=~/(?!^)[01]|8|9/)-i}


              Try it online!



              Explanation





              • ->n Take input as a string


              • z=n+".0" Create a temporary string z that is guaranteed to contain a dot and a relevant digit.


              • i=z=~/./ Determine the position of the decimal dot in z and assign to i.


              • z[i]='' Drop the dot so that it doesn't get in the way further on.


              • z=~/(?!^)[01]|8|9/ Determine the position of non-starting 0-1 or any 8-9, whichever comes first.


              • (...)-i This difference will be the number of decimal places to keep, negative if we will be rounding left of the dot.


              • n.to_f.round ... Convert to float and do the rounding.






              share|improve this answer











              $endgroup$
















                2












                2








                2





                $begingroup$


                Ruby, 79 77 69 67 65 bytes





                ->n,z=n+".0"{z[i=z=~/./]='';n.to_f.round (z=~/(?!^)[01]|8|9/)-i}


                Try it online!



                Explanation





                • ->n Take input as a string


                • z=n+".0" Create a temporary string z that is guaranteed to contain a dot and a relevant digit.


                • i=z=~/./ Determine the position of the decimal dot in z and assign to i.


                • z[i]='' Drop the dot so that it doesn't get in the way further on.


                • z=~/(?!^)[01]|8|9/ Determine the position of non-starting 0-1 or any 8-9, whichever comes first.


                • (...)-i This difference will be the number of decimal places to keep, negative if we will be rounding left of the dot.


                • n.to_f.round ... Convert to float and do the rounding.






                share|improve this answer











                $endgroup$




                Ruby, 79 77 69 67 65 bytes





                ->n,z=n+".0"{z[i=z=~/./]='';n.to_f.round (z=~/(?!^)[01]|8|9/)-i}


                Try it online!



                Explanation





                • ->n Take input as a string


                • z=n+".0" Create a temporary string z that is guaranteed to contain a dot and a relevant digit.


                • i=z=~/./ Determine the position of the decimal dot in z and assign to i.


                • z[i]='' Drop the dot so that it doesn't get in the way further on.


                • z=~/(?!^)[01]|8|9/ Determine the position of non-starting 0-1 or any 8-9, whichever comes first.


                • (...)-i This difference will be the number of decimal places to keep, negative if we will be rounding left of the dot.


                • n.to_f.round ... Convert to float and do the rounding.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Dec 9 '18 at 18:34

























                answered Dec 8 '18 at 16:46









                Kirill L.Kirill L.

                3,8151319




                3,8151319























                    1












                    $begingroup$


                    Jelly, 34 bytes



                    ;”.ḟ$µ»"”2e€⁽¡XṾ¤;1i1_i”.$_>¥0ɓVær


                    Try it online!



                    -1 thanks to Jonathan Allan.






                    share|improve this answer











                    $endgroup$













                    • $begingroup$
                      Why ŒV? I think V will work too.
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:15










                    • $begingroup$
                      @JonathanAllan Nope. (basically banker's rounding quirks)
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:16












                    • $begingroup$
                      Oh, because it's not acting on the input? Try _>¥0ɓVær like mine is (I missed usage of the dyadic quick so thanks too!)
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:24










                    • $begingroup$
                      @JonathanAllan Ah, clever usage of chains, thanks.
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:27
















                    1












                    $begingroup$


                    Jelly, 34 bytes



                    ;”.ḟ$µ»"”2e€⁽¡XṾ¤;1i1_i”.$_>¥0ɓVær


                    Try it online!



                    -1 thanks to Jonathan Allan.






                    share|improve this answer











                    $endgroup$













                    • $begingroup$
                      Why ŒV? I think V will work too.
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:15










                    • $begingroup$
                      @JonathanAllan Nope. (basically banker's rounding quirks)
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:16












                    • $begingroup$
                      Oh, because it's not acting on the input? Try _>¥0ɓVær like mine is (I missed usage of the dyadic quick so thanks too!)
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:24










                    • $begingroup$
                      @JonathanAllan Ah, clever usage of chains, thanks.
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:27














                    1












                    1








                    1





                    $begingroup$


                    Jelly, 34 bytes



                    ;”.ḟ$µ»"”2e€⁽¡XṾ¤;1i1_i”.$_>¥0ɓVær


                    Try it online!



                    -1 thanks to Jonathan Allan.






                    share|improve this answer











                    $endgroup$




                    Jelly, 34 bytes



                    ;”.ḟ$µ»"”2e€⁽¡XṾ¤;1i1_i”.$_>¥0ɓVær


                    Try it online!



                    -1 thanks to Jonathan Allan.







                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Dec 8 '18 at 18:27

























                    answered Dec 8 '18 at 16:23









                    Erik the OutgolferErik the Outgolfer

                    31.6k429103




                    31.6k429103












                    • $begingroup$
                      Why ŒV? I think V will work too.
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:15










                    • $begingroup$
                      @JonathanAllan Nope. (basically banker's rounding quirks)
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:16












                    • $begingroup$
                      Oh, because it's not acting on the input? Try _>¥0ɓVær like mine is (I missed usage of the dyadic quick so thanks too!)
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:24










                    • $begingroup$
                      @JonathanAllan Ah, clever usage of chains, thanks.
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:27


















                    • $begingroup$
                      Why ŒV? I think V will work too.
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:15










                    • $begingroup$
                      @JonathanAllan Nope. (basically banker's rounding quirks)
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:16












                    • $begingroup$
                      Oh, because it's not acting on the input? Try _>¥0ɓVær like mine is (I missed usage of the dyadic quick so thanks too!)
                      $endgroup$
                      – Jonathan Allan
                      Dec 8 '18 at 18:24










                    • $begingroup$
                      @JonathanAllan Ah, clever usage of chains, thanks.
                      $endgroup$
                      – Erik the Outgolfer
                      Dec 8 '18 at 18:27
















                    $begingroup$
                    Why ŒV? I think V will work too.
                    $endgroup$
                    – Jonathan Allan
                    Dec 8 '18 at 18:15




                    $begingroup$
                    Why ŒV? I think V will work too.
                    $endgroup$
                    – Jonathan Allan
                    Dec 8 '18 at 18:15












                    $begingroup$
                    @JonathanAllan Nope. (basically banker's rounding quirks)
                    $endgroup$
                    – Erik the Outgolfer
                    Dec 8 '18 at 18:16






                    $begingroup$
                    @JonathanAllan Nope. (basically banker's rounding quirks)
                    $endgroup$
                    – Erik the Outgolfer
                    Dec 8 '18 at 18:16














                    $begingroup$
                    Oh, because it's not acting on the input? Try _>¥0ɓVær like mine is (I missed usage of the dyadic quick so thanks too!)
                    $endgroup$
                    – Jonathan Allan
                    Dec 8 '18 at 18:24




                    $begingroup$
                    Oh, because it's not acting on the input? Try _>¥0ɓVær like mine is (I missed usage of the dyadic quick so thanks too!)
                    $endgroup$
                    – Jonathan Allan
                    Dec 8 '18 at 18:24












                    $begingroup$
                    @JonathanAllan Ah, clever usage of chains, thanks.
                    $endgroup$
                    – Erik the Outgolfer
                    Dec 8 '18 at 18:27




                    $begingroup$
                    @JonathanAllan Ah, clever usage of chains, thanks.
                    $endgroup$
                    – Erik the Outgolfer
                    Dec 8 '18 at 18:27











                    1












                    $begingroup$


                    Jelly,  30  29 bytes



                    -1 thanks to Erik the Outgolfer (use of dyadic quick ¥ from his answer)



                    O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær


                    A monadic link accepting a list of characters which yields a float.



                    Try it online! Or see the test-suite.



                    How



                    First note that the input string is made exclusively from the characters 0123456789. which have ordinals [48,49,50,51,52,53,54,55,56,57,46], which have remainders when divided by eight of [0,1,2,3,4,5,6,7,0,1,6]. The only characters which are between -1 and 1 inclusive are 0, 1, 8, and 9.



                    Furthermore if we subtract eight from the ordinals ([40,41,42,43,44,45,46,47,48,49,38]) the same (fairly obviously) holds. If we halve these ([20,20.5,21,21.5,22,22.5,23,23.5,24,24.5,19]) the only characters which have remainders when divided by eight which are between -1 and 1 inclusive are 8 and 9.



                    O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær - Link: list of characters, S
                    O - ordinal (vectorises across S)
                    ;0 - concatenate a zero
                    - (to cater BOTH for no '0', '1', '8', or '9' AND for no '.')
                    µ - start a new monadic link (call that X)
                    _8 - subtract eight (vectorises across X)
                    ¦ - sparse application...
                    1 - ...to: indices: one
                    H - ...do: halve (i.e. halve first ordinal)
                    %8 - modulo by eight (vectorises)
                    Ị - insignificant (abs(v)<=1?) (vectorises)
                    T - truthy indices
                    Ḣ - head
                    Ɗ - last three links as a monad (i.e. f(X)):
                    <48 - less than 48? (i.e. was it a '.' in S or the added 0?)
                    T - truthy indices
                    Ḣ - head
                    _ - subtract
                    ¥ - last two links as a dyad
                    < 0 - less than zero? (1 if so 0 otherwise)
                    _ - subtract
                    ɓ - start a new dyadic chain (i.e. f(S,X))
                    V - evaluate S as Jelly code (i.e. get S as a float)
                    ær - round to the nearest multiple of 10^(-X)





                    share|improve this answer











                    $endgroup$


















                      1












                      $begingroup$


                      Jelly,  30  29 bytes



                      -1 thanks to Erik the Outgolfer (use of dyadic quick ¥ from his answer)



                      O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær


                      A monadic link accepting a list of characters which yields a float.



                      Try it online! Or see the test-suite.



                      How



                      First note that the input string is made exclusively from the characters 0123456789. which have ordinals [48,49,50,51,52,53,54,55,56,57,46], which have remainders when divided by eight of [0,1,2,3,4,5,6,7,0,1,6]. The only characters which are between -1 and 1 inclusive are 0, 1, 8, and 9.



                      Furthermore if we subtract eight from the ordinals ([40,41,42,43,44,45,46,47,48,49,38]) the same (fairly obviously) holds. If we halve these ([20,20.5,21,21.5,22,22.5,23,23.5,24,24.5,19]) the only characters which have remainders when divided by eight which are between -1 and 1 inclusive are 8 and 9.



                      O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær - Link: list of characters, S
                      O - ordinal (vectorises across S)
                      ;0 - concatenate a zero
                      - (to cater BOTH for no '0', '1', '8', or '9' AND for no '.')
                      µ - start a new monadic link (call that X)
                      _8 - subtract eight (vectorises across X)
                      ¦ - sparse application...
                      1 - ...to: indices: one
                      H - ...do: halve (i.e. halve first ordinal)
                      %8 - modulo by eight (vectorises)
                      Ị - insignificant (abs(v)<=1?) (vectorises)
                      T - truthy indices
                      Ḣ - head
                      Ɗ - last three links as a monad (i.e. f(X)):
                      <48 - less than 48? (i.e. was it a '.' in S or the added 0?)
                      T - truthy indices
                      Ḣ - head
                      _ - subtract
                      ¥ - last two links as a dyad
                      < 0 - less than zero? (1 if so 0 otherwise)
                      _ - subtract
                      ɓ - start a new dyadic chain (i.e. f(S,X))
                      V - evaluate S as Jelly code (i.e. get S as a float)
                      ær - round to the nearest multiple of 10^(-X)





                      share|improve this answer











                      $endgroup$
















                        1












                        1








                        1





                        $begingroup$


                        Jelly,  30  29 bytes



                        -1 thanks to Erik the Outgolfer (use of dyadic quick ¥ from his answer)



                        O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær


                        A monadic link accepting a list of characters which yields a float.



                        Try it online! Or see the test-suite.



                        How



                        First note that the input string is made exclusively from the characters 0123456789. which have ordinals [48,49,50,51,52,53,54,55,56,57,46], which have remainders when divided by eight of [0,1,2,3,4,5,6,7,0,1,6]. The only characters which are between -1 and 1 inclusive are 0, 1, 8, and 9.



                        Furthermore if we subtract eight from the ordinals ([40,41,42,43,44,45,46,47,48,49,38]) the same (fairly obviously) holds. If we halve these ([20,20.5,21,21.5,22,22.5,23,23.5,24,24.5,19]) the only characters which have remainders when divided by eight which are between -1 and 1 inclusive are 8 and 9.



                        O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær - Link: list of characters, S
                        O - ordinal (vectorises across S)
                        ;0 - concatenate a zero
                        - (to cater BOTH for no '0', '1', '8', or '9' AND for no '.')
                        µ - start a new monadic link (call that X)
                        _8 - subtract eight (vectorises across X)
                        ¦ - sparse application...
                        1 - ...to: indices: one
                        H - ...do: halve (i.e. halve first ordinal)
                        %8 - modulo by eight (vectorises)
                        Ị - insignificant (abs(v)<=1?) (vectorises)
                        T - truthy indices
                        Ḣ - head
                        Ɗ - last three links as a monad (i.e. f(X)):
                        <48 - less than 48? (i.e. was it a '.' in S or the added 0?)
                        T - truthy indices
                        Ḣ - head
                        _ - subtract
                        ¥ - last two links as a dyad
                        < 0 - less than zero? (1 if so 0 otherwise)
                        _ - subtract
                        ɓ - start a new dyadic chain (i.e. f(S,X))
                        V - evaluate S as Jelly code (i.e. get S as a float)
                        ær - round to the nearest multiple of 10^(-X)





                        share|improve this answer











                        $endgroup$




                        Jelly,  30  29 bytes



                        -1 thanks to Erik the Outgolfer (use of dyadic quick ¥ from his answer)



                        O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær


                        A monadic link accepting a list of characters which yields a float.



                        Try it online! Or see the test-suite.



                        How



                        First note that the input string is made exclusively from the characters 0123456789. which have ordinals [48,49,50,51,52,53,54,55,56,57,46], which have remainders when divided by eight of [0,1,2,3,4,5,6,7,0,1,6]. The only characters which are between -1 and 1 inclusive are 0, 1, 8, and 9.



                        Furthermore if we subtract eight from the ordinals ([40,41,42,43,44,45,46,47,48,49,38]) the same (fairly obviously) holds. If we halve these ([20,20.5,21,21.5,22,22.5,23,23.5,24,24.5,19]) the only characters which have remainders when divided by eight which are between -1 and 1 inclusive are 8 and 9.



                        O;0µ_8H1¦%8ỊTḢ_<48TḢƊ_>¥0ɓVær - Link: list of characters, S
                        O - ordinal (vectorises across S)
                        ;0 - concatenate a zero
                        - (to cater BOTH for no '0', '1', '8', or '9' AND for no '.')
                        µ - start a new monadic link (call that X)
                        _8 - subtract eight (vectorises across X)
                        ¦ - sparse application...
                        1 - ...to: indices: one
                        H - ...do: halve (i.e. halve first ordinal)
                        %8 - modulo by eight (vectorises)
                        Ị - insignificant (abs(v)<=1?) (vectorises)
                        T - truthy indices
                        Ḣ - head
                        Ɗ - last three links as a monad (i.e. f(X)):
                        <48 - less than 48? (i.e. was it a '.' in S or the added 0?)
                        T - truthy indices
                        Ḣ - head
                        _ - subtract
                        ¥ - last two links as a dyad
                        < 0 - less than zero? (1 if so 0 otherwise)
                        _ - subtract
                        ɓ - start a new dyadic chain (i.e. f(S,X))
                        V - evaluate S as Jelly code (i.e. get S as a float)
                        ær - round to the nearest multiple of 10^(-X)






                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Dec 8 '18 at 18:55

























                        answered Dec 8 '18 at 18:17









                        Jonathan AllanJonathan Allan

                        51.2k534166




                        51.2k534166























                            1












                            $begingroup$


                            Retina 0.8.2, 75 bytes



                            ^[89]
                            10
                            T`d`0`(?<=.)[01].*|(?<=8|9).*
                            T`89d`0d`..?[89]
                            (.|(..+?))0+$
                            $2


                            Try it online! Link includes test cases. Explanation:



                            ^[89]
                            10


                            Handle the case of a leading 8 or 9.



                            T`d`0`(?<=.)[01].*|(?<=8|9).*


                            If there's a non-leading 0 or 1, then zero it and the rest of the string out. Also, if there's an 8 or 9, then leave it, but zero out the rest of the string. (But leave the decimal point unchanged in either case.)



                            T`89d`0d`..?[89]


                            If there's still an 8 or a 9 at this point, then zero it out, and increment the preceding digit (possibly before the decimal point).



                            (.|(..+?))0+$
                            $2


                            Delete trailing zeros if they are after a decimal point, but only delete the decimal point if there are no other digits in between.






                            share|improve this answer









                            $endgroup$


















                              1












                              $begingroup$


                              Retina 0.8.2, 75 bytes



                              ^[89]
                              10
                              T`d`0`(?<=.)[01].*|(?<=8|9).*
                              T`89d`0d`..?[89]
                              (.|(..+?))0+$
                              $2


                              Try it online! Link includes test cases. Explanation:



                              ^[89]
                              10


                              Handle the case of a leading 8 or 9.



                              T`d`0`(?<=.)[01].*|(?<=8|9).*


                              If there's a non-leading 0 or 1, then zero it and the rest of the string out. Also, if there's an 8 or 9, then leave it, but zero out the rest of the string. (But leave the decimal point unchanged in either case.)



                              T`89d`0d`..?[89]


                              If there's still an 8 or a 9 at this point, then zero it out, and increment the preceding digit (possibly before the decimal point).



                              (.|(..+?))0+$
                              $2


                              Delete trailing zeros if they are after a decimal point, but only delete the decimal point if there are no other digits in between.






                              share|improve this answer









                              $endgroup$
















                                1












                                1








                                1





                                $begingroup$


                                Retina 0.8.2, 75 bytes



                                ^[89]
                                10
                                T`d`0`(?<=.)[01].*|(?<=8|9).*
                                T`89d`0d`..?[89]
                                (.|(..+?))0+$
                                $2


                                Try it online! Link includes test cases. Explanation:



                                ^[89]
                                10


                                Handle the case of a leading 8 or 9.



                                T`d`0`(?<=.)[01].*|(?<=8|9).*


                                If there's a non-leading 0 or 1, then zero it and the rest of the string out. Also, if there's an 8 or 9, then leave it, but zero out the rest of the string. (But leave the decimal point unchanged in either case.)



                                T`89d`0d`..?[89]


                                If there's still an 8 or a 9 at this point, then zero it out, and increment the preceding digit (possibly before the decimal point).



                                (.|(..+?))0+$
                                $2


                                Delete trailing zeros if they are after a decimal point, but only delete the decimal point if there are no other digits in between.






                                share|improve this answer









                                $endgroup$




                                Retina 0.8.2, 75 bytes



                                ^[89]
                                10
                                T`d`0`(?<=.)[01].*|(?<=8|9).*
                                T`89d`0d`..?[89]
                                (.|(..+?))0+$
                                $2


                                Try it online! Link includes test cases. Explanation:



                                ^[89]
                                10


                                Handle the case of a leading 8 or 9.



                                T`d`0`(?<=.)[01].*|(?<=8|9).*


                                If there's a non-leading 0 or 1, then zero it and the rest of the string out. Also, if there's an 8 or 9, then leave it, but zero out the rest of the string. (But leave the decimal point unchanged in either case.)



                                T`89d`0d`..?[89]


                                If there's still an 8 or a 9 at this point, then zero it out, and increment the preceding digit (possibly before the decimal point).



                                (.|(..+?))0+$
                                $2


                                Delete trailing zeros if they are after a decimal point, but only delete the decimal point if there are no other digits in between.







                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered Dec 8 '18 at 19:54









                                NeilNeil

                                79.9k744178




                                79.9k744178























                                    1












                                    $begingroup$


                                    C (gcc), 111 102 bytes





                                    g(_,i,j,k)char*_;{for(i=*_<56?*_++:48,j=3;j;j&=k%8>1|(i=*_++)/48*2)putchar(j&1?i+(k=_[*_<48])/56:48);}


                                    Try it online!



                                    //_: input, as string
                                    //i: current digit, rounded if applicable
                                    //j: tracks whether number is rounded, and whether or '.' has been encountered
                                    //k: digit to test rounding (round if k is one of 0,1,8,9)
                                    //'0'==48, '8'==56
                                    g(_,i,j,k)char*_;{
                                    for(i=*_<56?*_++:48,j=3; //special case: if first digit is 8 or 9, use a
                                    //placeholder digit with value 0. initialize j.
                                    j; //only stop execution when number is rounded and
                                    //'.' or has been encountered.
                                    j&=k%8>1|(i=*_++)/48*2) //check if execution should stop.
                                    putchar(j&1?i+(k=_[*_<48])/56:48); //print '0' if rounding had already been done;
                                    //otherwise, print digit. round up as needed.
                                    }





                                    share|improve this answer











                                    $endgroup$


















                                      1












                                      $begingroup$


                                      C (gcc), 111 102 bytes





                                      g(_,i,j,k)char*_;{for(i=*_<56?*_++:48,j=3;j;j&=k%8>1|(i=*_++)/48*2)putchar(j&1?i+(k=_[*_<48])/56:48);}


                                      Try it online!



                                      //_: input, as string
                                      //i: current digit, rounded if applicable
                                      //j: tracks whether number is rounded, and whether or '.' has been encountered
                                      //k: digit to test rounding (round if k is one of 0,1,8,9)
                                      //'0'==48, '8'==56
                                      g(_,i,j,k)char*_;{
                                      for(i=*_<56?*_++:48,j=3; //special case: if first digit is 8 or 9, use a
                                      //placeholder digit with value 0. initialize j.
                                      j; //only stop execution when number is rounded and
                                      //'.' or has been encountered.
                                      j&=k%8>1|(i=*_++)/48*2) //check if execution should stop.
                                      putchar(j&1?i+(k=_[*_<48])/56:48); //print '0' if rounding had already been done;
                                      //otherwise, print digit. round up as needed.
                                      }





                                      share|improve this answer











                                      $endgroup$
















                                        1












                                        1








                                        1





                                        $begingroup$


                                        C (gcc), 111 102 bytes





                                        g(_,i,j,k)char*_;{for(i=*_<56?*_++:48,j=3;j;j&=k%8>1|(i=*_++)/48*2)putchar(j&1?i+(k=_[*_<48])/56:48);}


                                        Try it online!



                                        //_: input, as string
                                        //i: current digit, rounded if applicable
                                        //j: tracks whether number is rounded, and whether or '.' has been encountered
                                        //k: digit to test rounding (round if k is one of 0,1,8,9)
                                        //'0'==48, '8'==56
                                        g(_,i,j,k)char*_;{
                                        for(i=*_<56?*_++:48,j=3; //special case: if first digit is 8 or 9, use a
                                        //placeholder digit with value 0. initialize j.
                                        j; //only stop execution when number is rounded and
                                        //'.' or has been encountered.
                                        j&=k%8>1|(i=*_++)/48*2) //check if execution should stop.
                                        putchar(j&1?i+(k=_[*_<48])/56:48); //print '0' if rounding had already been done;
                                        //otherwise, print digit. round up as needed.
                                        }





                                        share|improve this answer











                                        $endgroup$




                                        C (gcc), 111 102 bytes





                                        g(_,i,j,k)char*_;{for(i=*_<56?*_++:48,j=3;j;j&=k%8>1|(i=*_++)/48*2)putchar(j&1?i+(k=_[*_<48])/56:48);}


                                        Try it online!



                                        //_: input, as string
                                        //i: current digit, rounded if applicable
                                        //j: tracks whether number is rounded, and whether or '.' has been encountered
                                        //k: digit to test rounding (round if k is one of 0,1,8,9)
                                        //'0'==48, '8'==56
                                        g(_,i,j,k)char*_;{
                                        for(i=*_<56?*_++:48,j=3; //special case: if first digit is 8 or 9, use a
                                        //placeholder digit with value 0. initialize j.
                                        j; //only stop execution when number is rounded and
                                        //'.' or has been encountered.
                                        j&=k%8>1|(i=*_++)/48*2) //check if execution should stop.
                                        putchar(j&1?i+(k=_[*_<48])/56:48); //print '0' if rounding had already been done;
                                        //otherwise, print digit. round up as needed.
                                        }






                                        share|improve this answer














                                        share|improve this answer



                                        share|improve this answer








                                        edited Dec 28 '18 at 5:26

























                                        answered Dec 27 '18 at 7:58









                                        attinatattinat

                                        3305




                                        3305























                                            0












                                            $begingroup$


                                            C# (Visual C# Interactive Compiler), 280 bytes





                                            c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=decimal.Parse(c);Func<decimal>q=()=>(decimal)Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                            Try it online!



                                            It can be shorter if I used doubles instead of decimals, but I used decimals to preserve accuracy, or else a number like 547.4726 would be 547.472595214844.




                                            C# (Visual C# Interactive Compiler), 268 bytes





                                            c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=float.Parse(c);Func<double>q=()=>Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                            Try it online!(Less accurate version)






                                            share|improve this answer











                                            $endgroup$


















                                              0












                                              $begingroup$


                                              C# (Visual C# Interactive Compiler), 280 bytes





                                              c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=decimal.Parse(c);Func<decimal>q=()=>(decimal)Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                              Try it online!



                                              It can be shorter if I used doubles instead of decimals, but I used decimals to preserve accuracy, or else a number like 547.4726 would be 547.472595214844.




                                              C# (Visual C# Interactive Compiler), 268 bytes





                                              c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=float.Parse(c);Func<double>q=()=>Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                              Try it online!(Less accurate version)






                                              share|improve this answer











                                              $endgroup$
















                                                0












                                                0








                                                0





                                                $begingroup$


                                                C# (Visual C# Interactive Compiler), 280 bytes





                                                c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=decimal.Parse(c);Func<decimal>q=()=>(decimal)Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                                Try it online!



                                                It can be shorter if I used doubles instead of decimals, but I used decimals to preserve accuracy, or else a number like 547.4726 would be 547.472595214844.




                                                C# (Visual C# Interactive Compiler), 268 bytes





                                                c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=float.Parse(c);Func<double>q=()=>Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                                Try it online!(Less accurate version)






                                                share|improve this answer











                                                $endgroup$




                                                C# (Visual C# Interactive Compiler), 280 bytes





                                                c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=decimal.Parse(c);Func<decimal>q=()=>(decimal)Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                                Try it online!



                                                It can be shorter if I used doubles instead of decimals, but I used decimals to preserve accuracy, or else a number like 547.4726 would be 547.472595214844.




                                                C# (Visual C# Interactive Compiler), 268 bytes





                                                c=>{int d=c.IndexOf('.');int t=c.IndexOfAny(new char{'8','9','0','1'},1);var m=c[0]=='8'||c[0]=='9'?1>0:0>1;var z=float.Parse(c);Func<double>q=()=>Math.Pow(10,m?d<0?c.Length:d:d<0?c.Length-t:d>t?d-t:d-t+1);return m?Math.Round(z/q())*q():t<0?z:Math.Round(z/q())*q();}


                                                Try it online!(Less accurate version)







                                                share|improve this answer














                                                share|improve this answer



                                                share|improve this answer








                                                edited Dec 11 '18 at 21:24

























                                                answered Dec 11 '18 at 4:01









                                                Embodiment of IgnoranceEmbodiment of Ignorance

                                                691115




                                                691115






























                                                    draft saved

                                                    draft discarded




















































                                                    If this is an answer to a challenge…




                                                    • …Be sure to follow the challenge specification. However, please refrain from exploiting obvious loopholes. Answers abusing any of the standard loopholes are considered invalid. If you think a specification is unclear or underspecified, comment on the question instead.


                                                    • …Try to optimize your score. For instance, answers to code-golf challenges should attempt to be as short as possible. You can always include a readable version of the code in addition to the competitive one.
                                                      Explanations of your answer make it more interesting to read and are very much encouraged.


                                                    • …Include a short header which indicates the language(s) of your code and its score, as defined by the challenge.



                                                    More generally…




                                                    • …Please make sure to answer the question and provide sufficient detail.


                                                    • …Avoid asking for help, clarification or responding to other answers (use comments instead).





                                                    draft saved


                                                    draft discarded














                                                    StackExchange.ready(
                                                    function () {
                                                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f177203%2fsatisfying-rounding%23new-answer', 'question_page');
                                                    }
                                                    );

                                                    Post as a guest















                                                    Required, but never shown





















































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown

































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown







                                                    Popular posts from this blog

                                                    Sphinx de Gizeh

                                                    Dijon

                                                    Guerrita