C Penerapan atof

Aug 21 2020

Saya seorang pemula di C. Saat ini saya menerapkan atof untuk membuat raytracer, namun saya masih belajar cara menulis program secara efisien.

Penugasan

Instruksi

Program mengambil file deskripsi adegan sebagai argumen untuk menghasilkan objek. Beberapa dari parameter ini mengapung. Contoh file.

Saya sedang mem-parsing file. Karena saya dibatasi dalam hal garis yang diizinkan per fungsi dan saya sedang mempelajari cara kerja penunjuk ganda, saya menggunakan penunjuk karakter ganda. Contoh salah satu fungsi tersebut menggunakan lc_atof.

int    a_parsing(char *str, t_pars *data)
{
    if (*(str++) == 'A')
    {
        if (((data->a_ratio = lc_atof(&str)) >= 0.0) && data->a_ratio <= 1.0 && errno == 0)
        // 
            if (((data->a_R = lc_atoi(&str)) >= 0) && data->a_R <= 255 && errno == 0)
                if (*(str++) = ',' && ((data->a_G = lc_atoi(&str)) >= 0) && data->a_G <= 255 && errno == 0)
                    if (*(str++) = ',' && ((data->a_B = lc_atoi(&str)) >= 0) && data->a_B <= 255 && errno == 0)
                        return (skip_space(&str));
    }
    return (0);
}


Kode saat ini untuk ditinjau:

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <float.h>

static float    conversion(char **str)
{
    double    d_nbr;
    double    power;

    d_nbr = 0.0;
    power = 10.0;
    while (isdigit(**str))
    {
        d_nbr = d_nbr * 10.0 + (**str - 48);
        if (d_nbr > FLT_MAX)
        {
            errno = EIO;
            return (-1);
        }
        (*str)++;
    }
    if (**str == '.')
    {
      (*str)++;
      if (isdigit(**str))
      {
        d_nbr = d_nbr * 10.0 + (**str - 48);
        if (d_nbr > FLT_MAX)
        {
            errno = EIO;
            return (-1);
        }
        (*str)++;
        return ((float)(d_nbr / power));
      }
    }
    errno = EIO;
    return (-1);
}

float            lc_atof(char **str)
{
    float    n;
    int        sign;

    n = 0.0;
    sign = 1.0;
    if (!str || !*str)
    {
        errno = EIO;
        return (-1);
    }
    while (isspace(**str))
        (*str)++;
    if (**str == '+' || **str == '-')
    {
        if (**str == '-')
            sign = -1.0;    
        (*str)++;
    }
    if (!isdigit(**str))
    {
        errno = EIO;
        return (-1);
    }
    if ((n = conversion(str)) == 0 && errno != 0)
       return (-1);
    return (sign * n);
}

Satu-satunya perubahan pada atof aktual yang saya buat adalah memiliki penunjuk karakter ganda sebagai argumen dan mengembalikan -1 jika terjadi kesalahan.

Setiap masukan sangat dihargai.

Jawaban

10 pacmaninbw Aug 21 2020 at 01:38

Portabilitas

Tidak ada jaminan bahwa kode ini akan menggunakan ASCII jadi akan lebih baik digunakan '0'daripada 48yang merupakan angka ajaib. Menggunakan '0'membuatnya lebih mudah dibaca dan dipahami.

lc_atof Tidak Menangani Penghentian String atau Akhir Baris dengan Benar

Kode ini tidak menangani string yang diakhiri NULL atau karakter akhir baris. Fungsi ini isspace()kembali trueuntuk akhir baris sehingga kode akan berjalan melewatinya.

    while (isspace(**str))
        (*str)++;
    if (**str == '+' || **str == '-')
    {
        if (**str == '-')
            sign = -1.0;
        (*str)++;
    }
    if (!isdigit(**str))
    {
        errno = EIO;
        return (-1);
    }

Kompleksitas

Saya yakin bahwa Anda tidak meminta ini untuk ditinjau, tetapi kompleksitas setiap ifpernyataan dalam contoh fungsi panggilan terlalu banyak dan menyebabkan saya membuat kesalahan dalam tinjauan saya sebelumnya:

int a_parsing(char* str, t_pars* data)
{
    if (*(str++) == 'A')
    {
        if (((data->a_ratio = lc_atof(&str)) >= 0.0) && data->a_ratio <= 1.0 && errno == 0)
            // 
            if (((data->a_R = lc_atoi(&str)) >= 0) && data->a_R <= 255 && errno == 0)
                if (*(str++) = ',' && ((data->a_G = lc_atoi(&str)) >= 0) && data->a_G <= 255 && errno == 0)
                    if (*(str++) = ',' && ((data->a_B = lc_atoi(&str)) >= 0) && data->a_B <= 255 && errno == 0)
                        return (skip_space(&str));
    }
    return (0);
}

Saya akan menulis ulang kode sebagai:

#define MAX_COLOR   0xFF
int a_parsing_prime(char* str, t_pars* data)
{
    if (*(str++) == 'A')
    {
        data->a_ratio = lc_atof(&str);
        if (!errno && data->a_R <= MAX_COLOR)
        {
            if (*(str++) = ',')
            {
                data->a_G = lc_atoi(&str);
                if (!errno && data->a_G <= MAX_COLOR)
                {
                    if (*(str++) = ',')
                    {
                        data->a_B = lc_atoi(&str);
                        if (!errno && data->a_B <= MAX_COLOR)
                        {
                            return (skip_space(&str));
                        }
                    }
                }
            }
        }
    }
    return (0);
}

yang benar-benar menunjukkan kompleksitas fungsi tersebut.

10 vnp Aug 21 2020 at 06:19
  • Memilih EIOpelaporan kesalahan sangat meragukan. lc_atoftidak melakukan input atau output apa pun; mengapa harus melaporkan kesalahan IO? Jika tipe kembalian tidak dapat mewakili hasil (misalnya d_nbr > FLT_MAX), pilihan logisnya adalah ERANGEatau EOVERFLOW. Jika konversi tidak dapat diselesaikan karena format argumen yang salah (misalnya !isdigit(**str)), pilihan logisnya mungkin EINVAL.

    Karena itu, saya tidak mendukung pengaturan errnodi fungsi perpustakaan. Tradisi lama adalah mengatur errnodalam panggilan sistem saja. Saya tahu bahwa tradisi ini semakin banyak dilanggar akhir-akhir ini, tapi tetap saja. Jika Anda memiliki cara lain untuk melaporkan kesalahan, tetaplah menggunakannya.

  • Menggunakan parameter inout ( strdalam kasus Anda) tidak disarankan. Ini tidak perlu memperumit kode, baik di sisi pemanggil dan sisi callee. Callee dipaksa untuk menggunakan tipuan ekstra terlalu banyak, dan khawatir tentang tanda kurung (**str)++. Pada gilirannya, pemanggil kehilangan jejak di mana parsable dimulai (katakanlah, ia perlu mencatat nomor yang salah format). Lihat bagaimana strtofmenangani ini:

      float strtof(const char *restrict nptr, char **restrict endptr);
    

    Berikut nptradalah in-only, dan endptrout-only.

  • Saya terkejut bahwa Anda memutuskan untuk membatasi kegunaan fungsi dengan menangani hanya satu digit setelah titik desimal. Ini bukanlah upaya yang besar untuk menangani semuanya, dan manfaatnya jauh lebih besar.

  • Tidak perlu memberi tanda kurung nilai yang dikembalikan. returnadalah operator, bukan fungsi.