1  /* Test for correct rounding of printf floating-point output.
       2     Copyright (C) 2012-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <array_length.h>
      20  #include <fenv.h>
      21  #include <stdio.h>
      22  #include <string.h>
      23  
      24  struct dec_test {
      25    double d;
      26    const char *fmt;
      27    const char *rd, *rn, *rz, *ru;
      28  };
      29  
      30  static const struct dec_test dec_tests[] = {
      31    { 1.5, "%.0f", "1", "2", "1", "2" },
      32    { -1.5, "%.0f", "-2", "-2", "-1", "-1" },
      33    { 2.5, "%.0f", "2", "2", "2", "3" },
      34    { -2.5, "%.0f", "-3", "-2", "-2", "-2" },
      35    { 1.4999, "%.0f", "1", "1", "1", "2" },
      36    { -1.4999, "%.0f", "-2", "-1", "-1", "-1" },
      37    { 1.5001, "%.0f", "1", "2", "1", "2" },
      38    { -1.5001, "%.0f", "-2", "-2", "-1", "-1" },
      39    { 2.4999, "%.0f", "2", "2", "2", "3" },
      40    { -2.4999, "%.0f", "-3", "-2", "-2", "-2" },
      41    { 2.5001, "%.0f", "2", "3", "2", "3" },
      42    { -2.5001, "%.0f", "-3", "-3", "-2", "-2" },
      43    { 1.0 / 3.0, "%f", "0.333333", "0.333333", "0.333333", "0.333334" },
      44    { -1.0 / 3.0, "%f", "-0.333334", "-0.333333", "-0.333333", "-0.333333" },
      45    { 0.2500001, "%.2e", "2.50e-01", "2.50e-01", "2.50e-01", "2.51e-01" },
      46    { -0.2500001, "%.2e", "-2.51e-01", "-2.50e-01", "-2.50e-01", "-2.50e-01" },
      47    { 1000001.0, "%.1e", "1.0e+06", "1.0e+06", "1.0e+06", "1.1e+06" },
      48    { -1000001.0, "%.1e", "-1.1e+06", "-1.0e+06", "-1.0e+06", "-1.0e+06" },
      49  };
      50  
      51  static int
      52  test_dec_in_one_mode (double d, const char *fmt, const char *expected,
      53  		      const char *mode_name)
      54  {
      55    char buf[100];
      56    int ret = snprintf (buf, sizeof buf, fmt, d);
      57    if (ret <= 0 || ret >= (int) sizeof buf)
      58      {
      59        printf ("snprintf for %a returned %d\n", d, ret);
      60        return 1;
      61      }
      62    if (strcmp (buf, expected) == 0)
      63      return 0;
      64    else
      65      {
      66        printf ("snprintf (\"%s\", %a) returned \"%s\" not \"%s\" (%s)\n",
      67  	      fmt, d, buf, expected, mode_name);
      68        return 1;
      69      }
      70  }
      71  
      72  struct hex_test
      73  {
      74    double d;
      75    const char *fmt;
      76    const char *rd[4], *rn[4], *rz[4], *ru[4];
      77  };
      78  
      79  static const struct hex_test hex_tests[] =
      80    {
      81      {
      82        0x1.fffffp+4, "%.1a",
      83        { "0x1.fp+4", "0x3.fp+3", "0x7.fp+2", "0xf.fp+1" },
      84        { "0x2.0p+4", "0x4.0p+3", "0x8.0p+2", "0x1.0p+5" },
      85        { "0x1.fp+4", "0x3.fp+3", "0x7.fp+2", "0xf.fp+1" },
      86        { "0x2.0p+4", "0x4.0p+3", "0x8.0p+2", "0x1.0p+5" }
      87      },
      88      {
      89        -0x1.fffffp+4, "%.1a",
      90        { "-0x2.0p+4", "-0x4.0p+3", "-0x8.0p+2", "-0x1.0p+5" },
      91        { "-0x2.0p+4", "-0x4.0p+3", "-0x8.0p+2", "-0x1.0p+5" },
      92        { "-0x1.fp+4", "-0x3.fp+3", "-0x7.fp+2", "-0xf.fp+1" },
      93        { "-0x1.fp+4", "-0x3.fp+3", "-0x7.fp+2", "-0xf.fp+1" }
      94      },
      95      {
      96        0x1.88p+4, "%.1a",
      97        { "0x1.8p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" },
      98        { "0x1.8p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" },
      99        { "0x1.8p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" },
     100        { "0x1.9p+4", "0x3.1p+3", "0x6.2p+2", "0xc.4p+1" }
     101      },
     102      {
     103        -0x1.88p+4, "%.1a",
     104        { "-0x1.9p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" },
     105        { "-0x1.8p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" },
     106        { "-0x1.8p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" },
     107        { "-0x1.8p+4", "-0x3.1p+3", "-0x6.2p+2", "-0xc.4p+1" }
     108      },
     109      {
     110        0x1.78p+4, "%.1a",
     111        { "0x1.7p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" },
     112        { "0x1.8p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" },
     113        { "0x1.7p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" },
     114        { "0x1.8p+4", "0x2.fp+3", "0x5.ep+2", "0xb.cp+1" }
     115      },
     116      {
     117        -0x1.78p+4, "%.1a",
     118        { "-0x1.8p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" },
     119        { "-0x1.8p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" },
     120        { "-0x1.7p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" },
     121        { "-0x1.7p+4", "-0x2.fp+3", "-0x5.ep+2", "-0xb.cp+1" }
     122      },
     123      {
     124        64.0 / 3.0, "%.1a",
     125        { "0x1.5p+4", "0x2.ap+3", "0x5.5p+2", "0xa.ap+1" },
     126        { "0x1.5p+4", "0x2.bp+3", "0x5.5p+2", "0xa.bp+1" },
     127        { "0x1.5p+4", "0x2.ap+3", "0x5.5p+2", "0xa.ap+1" },
     128        { "0x1.6p+4", "0x2.bp+3", "0x5.6p+2", "0xa.bp+1" }
     129      },
     130      {
     131        -64.0 / 3.0, "%.1a",
     132        { "-0x1.6p+4", "-0x2.bp+3", "-0x5.6p+2", "-0xa.bp+1" },
     133        { "-0x1.5p+4", "-0x2.bp+3", "-0x5.5p+2", "-0xa.bp+1" },
     134        { "-0x1.5p+4", "-0x2.ap+3", "-0x5.5p+2", "-0xa.ap+1" },
     135        { "-0x1.5p+4", "-0x2.ap+3", "-0x5.5p+2", "-0xa.ap+1" }
     136      },
     137    };
     138  
     139  static int
     140  test_hex_in_one_mode (double d, const char *fmt, const char *const expected[4],
     141  		      const char *mode_name)
     142  {
     143    char buf[100];
     144    int ret = snprintf (buf, sizeof buf, fmt, d);
     145    if (ret <= 0 || ret >= (int) sizeof buf)
     146      {
     147        printf ("snprintf for %a returned %d\n", d, ret);
     148        return 1;
     149      }
     150    if (strcmp (buf, expected[0]) == 0
     151        || strcmp (buf, expected[1]) == 0
     152        || strcmp (buf, expected[2]) == 0
     153        || strcmp (buf, expected[3]) == 0)
     154      return 0;
     155    else
     156      {
     157        printf ("snprintf (\"%s\", %a) returned \"%s\" not "
     158  	      "\"%s\" or \"%s\" or \"%s\" or \"%s\" (%s)\n",
     159  	      fmt, d, buf, expected[0], expected[1], expected[2], expected[3],
     160  	      mode_name);
     161        return 1;
     162      }
     163  }
     164  
     165  static int
     166  do_test (void)
     167  {
     168    int save_round_mode __attribute__ ((unused)) = fegetround ();
     169    int result = 0;
     170  
     171    for (size_t i = 0; i < array_length (dec_tests); i++)
     172      {
     173        result |= test_dec_in_one_mode (dec_tests[i].d, dec_tests[i].fmt,
     174  				      dec_tests[i].rn, "default rounding mode");
     175  #ifdef FE_DOWNWARD
     176        if (!fesetround (FE_DOWNWARD))
     177  	{
     178  	  result |= test_dec_in_one_mode (dec_tests[i].d, dec_tests[i].fmt,
     179  					  dec_tests[i].rd, "FE_DOWNWARD");
     180  	  fesetround (save_round_mode);
     181  	}
     182  #endif
     183  #ifdef FE_TOWARDZERO
     184        if (!fesetround (FE_TOWARDZERO))
     185  	{
     186  	  result |= test_dec_in_one_mode (dec_tests[i].d, dec_tests[i].fmt,
     187  					  dec_tests[i].rz, "FE_TOWARDZERO");
     188  	  fesetround (save_round_mode);
     189  	}
     190  #endif
     191  #ifdef FE_UPWARD
     192        if (!fesetround (FE_UPWARD))
     193  	{
     194  	  result |= test_dec_in_one_mode (dec_tests[i].d, dec_tests[i].fmt,
     195  					  dec_tests[i].ru, "FE_UPWARD");
     196  	  fesetround (save_round_mode);
     197  	}
     198  #endif
     199      }
     200  
     201    for (size_t i = 0; i < array_length (hex_tests); i++)
     202      {
     203        result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt,
     204  				      hex_tests[i].rn, "default rounding mode");
     205  #ifdef FE_DOWNWARD
     206        if (!fesetround (FE_DOWNWARD))
     207  	{
     208  	  result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt,
     209  					  hex_tests[i].rd, "FE_DOWNWARD");
     210  	  fesetround (save_round_mode);
     211  	}
     212  #endif
     213  #ifdef FE_TOWARDZERO
     214        if (!fesetround (FE_TOWARDZERO))
     215  	{
     216  	  result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt,
     217  					  hex_tests[i].rz, "FE_TOWARDZERO");
     218  	  fesetround (save_round_mode);
     219  	}
     220  #endif
     221  #ifdef FE_UPWARD
     222        if (!fesetround (FE_UPWARD))
     223  	{
     224  	  result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt,
     225  					  hex_tests[i].ru, "FE_UPWARD");
     226  	  fesetround (save_round_mode);
     227  	}
     228  #endif
     229      }
     230  
     231    return result;
     232  }
     233  
     234  #define TEST_FUNCTION do_test ()
     235  #include "../test-skeleton.c"