toge's diary

コンピュータ関連の趣味をつらつらと。

printfを使わずに実数を表示する

printf("%f")より速くしようと思ってざっと作ったら、返ってprintfより遅かった。
std::stringなんか使っちゃ行かんのだな。printfの実装を参考にしよう。(それすらやってないんかいっ!)

#include <iostream>
#include <string>
#include <cmath>

static
std::string
i2s(int value, int keta)
{
  std::string str;
  str.reserve(keta);
  for (int index = 0; index < keta; ++index)
  {
    str.insert(0, 1, '0' + (value % 10));
    value /= 10;
  }
  return str;
}

std::string
f2s(double value, int keta, int precision = 6, bool sign = false, char padding = ' ', char separate = '.')
{
  std::string result;
  result.reserve(keta);

  int part_int = std::floor(value);
  double part_float = value - part_int;
  int part_int_length = 0;

  for (int index = 1; index < part_int ; index *= 10)
  {
    part_int_length++;
  }

  int part_int_max;
  if (keta < precision)
  {
    part_int_max = 0;
  }
  else
  {
    part_int_max = keta - 1 - precision;
  }

  if (sign == true || value < 0)
  {
    if (value < 0)
    {
      result = '-';
    }
    else
    {
      result = '+';
    }
    part_int_max--;
  }

  for (int index = 0; index < part_int_max - part_int_length; ++index)
  {
    result += padding;
  }
  result += i2s(part_int, part_int_length) + separate;

  for (int index = 0; index < precision; index++)
  {
    part_float *= 10;
  }

  result += i2s((int)part_float, precision);

  return result;
}

template <int v1>
class POWER10
{
public:
  enum
  {
    value = POWER10<v1 - 1>::value * 10
  };
};

template<>
class POWER10<1>
{
public:
  enum
  {
    value = 10
  };
};

template <int KETA, int PRESITION, bool SIGN = false, char PADDING = ' ', char SEPARATE = '.'>
class
FloatFormatterFix
{
private:
  static const int PART_INT_MAX = KETA - 1 - PRESITION;
  static const int PART_FLOAT_MULTI = POWER10<PRESITION>::value;

public:
  FloatFormatterFix()
  {}

public:
  std::string operator()(double value) const
  {
    std::string result;
    result.reserve(KETA);
    int part_int = floor(value);
    double part_float = value - part_int;

    int part_int_length = 0;
    for (int index = 1; index < part_int ; index *= 10)
    {
      part_int_length++;
    }

    if (SIGN == true || value < 0)
    {
      if (value < 0)
      {
        result = '-';
      }
      else
      {
        result = '+';
      }
      part_int_length++;
    }

    for (int index = 0; index < PART_INT_MAX - part_int_length; ++index)
    {
      result += PADDING;
    }

    result += i2s(part_int, part_int_length) + SEPARATE;
    result += i2s((int)(part_float * PART_FLOAT_MULTI), PRESITION);

    return result;
  }
};

#include "sys/time.h"


int main()
{
  const int LOOP = 1000000;
  struct timeval start, end;

  gettimeofday(&start, NULL);
  printf("%ld.%06d\n", start.tv_sec, start.tv_usec);

  char buf[20];
  for (int index = 0; index < LOOP; ++index)
  {
    snprintf(buf, 20, "%10.8f", 13.5343);
  }

  gettimeofday(&end, NULL);
  printf("%d.%06d\n", end.tv_sec, end.tv_usec);


  gettimeofday(&start, NULL);
  printf("%ld.%06d\n", start.tv_sec, start.tv_usec);

  for (int index = 0; index < LOOP; ++index)
  {
    std::string a = f2s(13.5343, 10,8);
  }

  gettimeofday(&end, NULL);
  printf("%d.%06d\n", end.tv_sec, end.tv_usec);

  gettimeofday(&start, NULL);
  printf("%ld.%06d\n", start.tv_sec, start.tv_usec);

  FloatFormatterFix<10, 8, false, ' ', ';'> formatter;
  for (int index = 0; index < LOOP; ++index)
  {
    std::string a = formatter(13.5343);
  }

  gettimeofday(&end, NULL);
  printf("%d.%06d\n", end.tv_sec, end.tv_usec);


  return 0;
}